
В Питоне совсем не важно, что вы поместите в определение класса. Это могут быть строки, числа, объекты, переменные. В третьем Питоне можно даже передавать именованные аргументы.
Реализация
Сначала необходимо разочаровать: в Питоне нельзя наследовать не от классов, т.е. нельзя передавать конструктору классов
type
параметр bases
, в котором есть неклассы. Однако нигде не оговорено, что вы будете включать в определение класса.Ничего сложно в реализации кода выше нет, для этого просто потребуется создать метакласс, в котором будут отфильтровываться неклассы из
bases
(которые можно дальше использовать по своему усмотрению). Думаю, пример все объяснит, copy&run:Copy Source | Copy HTML
- # coding: utf-8
-
- class Metaclass(type):
-
- '''<br/> Metaclass печатает все объекты, от которых наследует класс<br/> и которые не являеются классами.<br/> '''
-
- def __new__(cls, name, bases, dict):
- import inspect
-
- new_bases = []
- for base in bases:
- # отфильтруем классы,
- # остальное просто напечатаем
- if inspect.isclass(base):
- new_bases.append(base)
- else:
- print base
-
- return type.__new__(cls, name, tuple(new_bases), dict)
-
-
- class Print(object):
-
- __metaclass__ = Metaclass
-
-
- class String(Print, 'Programming', 'is', 'all', 'about', 'architecture.', ''):
- pass
-
- class Numbers(Print, 0, 1, 2.718, 3.1459, ''):
- pass
-
- class More(Print, None, True, 'or', False, ['to be', 'or', 'not to be'], ''):
- pass
-
- the_end = 'The end.'
- class End(Print, the_end):
- pass
Использование именованных аргументов в определении класса в третьем Питоне, copy&run:
Copy Source | Copy HTML
- # Python 3+
-
- def metaclass(name, bases, dict, private=True):
- print('Private:', private)
- return type(name, bases, dict)
-
-
- class MyClass(object, metaclass=metaclass, private=False):
-
- pass
Практическая целесообразность
В большинстве случаев эта возможность вообще не требуется. Более того, код становится сложным и «магическим», что совсем не приветствуется. Однако в некоторых случаях она оказывается незаменима, как и метаклассы в целом. По сути, можно выделить два преимущества:
- Управление метаклассами и
- Упрощение API.
Управление конструкцией классов (и вообще метаклассами)
Передача параметров для метакласса непосредственно в определении класса. Конечно, можно передать те же самые параметры через атрибуты обычного класса, но это засоряет класс и не всегда выглядит читабельно. Например, пусть необходимо наследовать либо полный бокал, либо пустой (пример притянут за уши, простого и понятного не придумал, а взять из готового кода не получилось). Copy&run:
Copy Source | Copy HTML
- #coding: utf-8
-
- class EmptyGlass: pass
- class FullGlass: pass
-
-
- class GlassMetaclass(type):
-
- '''<br/> GlassMetaclass создает либо класс EmptyGlass, либо FullGlass,<br/> в зависимости от того, было ли указано True или False в определении<br/> класса.<br/> '''
-
- empty_glass = EmptyGlass
- full_glass = FullGlass
-
- def __new__(cls, name, bases, dict):
- if bases[-1] is True: # is True, потому что нам надо именно объект True
- # полный бокал
- bases = [cls.full_glass] + list(bases[:-1])
- elif bases[-1] is False:
- # пустой
- bases = [cls.empty_glass] + list(bases[:-1])
-
- return super(GlassMetaclass, cls).__new__(cls, name, tuple(bases), dict)
-
-
- class Glass(object):
-
- __metaclass__ = GlassMetaclass
-
-
- if __name__ == '__main__':
- class MyGlass(Glass, True): pass # полный стакан
- class YourGlass(Glass, False): pass # пустой стакан
-
- print issubclass(MyGlass, FullGlass) # True
- print issubclass(YourGlass, EmptyGlass) # True
Упрощение API
Допустим, нам требуется создать дерево из классов (не структуру данных, а просто иерархию). Пусть это будет иерархические регулярные выражения. Каждому классу в этом случае требуется передать строку, которая будет скомпилирована в регулярное выражение. В классическом виде этом будет выглядеть примерно так:
Copy Source | Copy HTML
- class Music(MyRe):
-
- pattern = '/music'
-
- class Artist(MyRe):
-
- pattern = '/artist/\d+'
-
- class Song(MyRe):
-
- pattern = '/song/\d+'
-
- class Album(MyRe):
-
- pattern = '/album/\d+'
Включение строк прямо в определение классов позволяет получить более красивый и понятный код:
Copy Source | Copy HTML
- class Music(MyRe, '/music'):
-
- class Artist(MyRe, '/artist/\d+'): pass
- class Song(MyRe, '/song/\d+'): pass
- class Album(MyRe, '/album/\d+'): pass
Заключение
Передача параметров метаклассам в определении классов, с одной стороны, усложняет реализацию кода программы, с другой стороны, позволяет построить более человечный интерфейс и в дальнейшем фокусироваться на решении проблем. Другими словами, быть на шаг ближе к DSL.
Дополнительная литература по метаклассам
- Официальная документация, Customizing class creation. docs.python.org/3.1/reference/datamodel.html#customizing-class-creation
- Unifying types and classes in Python 2.2 #Metaclasses, Guido van Rossum. www.python.org/download/releases/2.2/descrintro/#metaclasses
- PEP-3115, Metaclasses in Python 3000. www.python.org/dev/peps/pep-3115
- Python Metaclasess: Who? Why? When?, Mike C. Fletcher at PyCon 2004, презентация. www.vrplumber.com/programming/metaclasses-pycon.pdf