Метод вашего класса __init__
добавляет связанный метод в качестве атрибута для экземпляров вашего класса. Это не то же самое, что добавить атрибут в класс. Обычно методы работают, сохраняя функции в классе как атрибуты, а затем создавая объекты методов , поскольку эти функции извлекаются как атрибуты либо из класса (создавая несвязанные методы, которые знают только класс, к которому они принадлежат), либо экземпляр (создание связанных методов, которые знают их экземпляр.)
Чем это отличается от того, что вы делаете? Ну, вы присваиваете GET
экземпляр атрибута конкретного экземпляра, а не класса. Связанный метод становится частью данных экземпляра:
>>> s.__dict__
{'GET': <bound method Sub.get of <__main__.Sub object at 0xb70896cc>>}
Обратите внимание, что метод находится под ключом GET
, но не под get
. GET
является атрибутом экземпляра, а get
- нет. Это несколько отличается по ряду причин: метод не существует в объекте класса, поэтому вы не можете Sub.GET(instance)
вызвать Sub
метод GET
, даже если вы можете сделать Sub.get(instance)
, Во-вторых, если у вас есть подкласс Sub, который определяет свой собственный GET
метод , но не свой собственный get
метод , атрибут экземпляра скроет подкласс GET
метод с связанный get
метод из базового класса. В-третьих, он создает циклическую ссылку между связанным методом и экземпляром: связанный метод имеет ссылку на экземпляр, и теперь экземпляр сохраняет ссылку на связанный метод. Обычно связанные методы не сохраняются в экземпляре частично, чтобы избежать этого. Циркулярные ссылки обычно не являются большой проблемой, потому что в настоящее время у нас есть модуль cyclic-gc (gc
), который заботится о них, но он не всегда может очистить циклы ссылок (например, когда ваш класс также имеет __del__
method.) И, наконец, хранение объектов связанных методов обычно делает ваши экземпляры не сериализуемыми: большинство сериализаторов (таких как pickle
) не могут обрабатывать связанные методы.
Вам может быть наплевать на любую из этих проблем, но если вы это сделаете, есть лучший подход к тому, что вы пытаетесь сделать: метаклассы. Вместо того, чтобы назначать связанные методы атрибутам экземпляра при создании экземпляров, вы можете назначать обычные функции атрибутам класса при создании класса:
class MethodAliasingType(type):
def __init__(self, name, bases, attrs):
# attrs is the dict of attributes that was used to create the
# class 'self', modifying it has no effect on the class.
# So use setattr() to set the attribute.
for k, v in attrs.iteritems():
if not hasattr(self, k.upper()):
setattr(self, k.upper(), v)
super(MethodAliasingType, self).__init__(name, bases, attrs)
class Base(object):
__metaclass__ = MethodAliasingType
class Sub(Base):
def get(self):
pass
Теперь Sub.get
и Sub.GET
действительно являются псевдонимами, и переопределение одного, а не другого в подклассе работает, как и ожидалось.
>>> Sub.get
<unbound method Sub.get>
>>> Sub.GET
<unbound method Sub.get>
>>> Sub().get
<bound method Sub.get of <__main__.Sub object at 0xb708978c>>
>>> Sub().GET
<bound method Sub.get of <__main__.Sub object at 0xb7089a6c>>
>>> Sub().__dict__
{}
(Конечно, если вы не хотите, чтобы переопределял один, а другой не работал, вы можете просто сделать это ошибкой в своем метаклассе.) Вы можете сделать то же самое, что и метакласс использование декораторов классов (в Python 2.6 и более поздних версиях), но это будет означать, что декораторы классов в каждом подклассе базовых классов не должны наследоваться.