2010-04-28 2 views
2

Я хочу добавить методы (более конкретно: псевдонимы методов) автоматически в подклассы Python. Если подкласс определяет метод с именем «get», я хочу добавить псевдоним метода «GET» в словарь подкласса.добавить методы в подклассы в конструкторе суперкласса

Чтобы не повториться, я бы хотел определить эту процедуру модификации в базовом классе. Но если я проверю метод базового класса __init__, такого метода нет, поскольку он определен в подклассе. Это станет более ясным, с некоторым исходным кодом:

class Base: 

    def __init__(self): 
     if hasattr(self, "get"): 
      setattr(self, "GET", self.get) 


class Sub(Base): 

    def get(): 
     pass 


print(dir(Sub)) 

Выход:

['__doc__', '__init__', '__module__', 'get'] 

Он должен также содержать 'GET'.

Есть ли способ сделать это в базовом классе?

ответ

2

__init__ метод вашего класса добавляет связанный метод в качестве атрибута в случаях вашего класса. Это не совсем то же самое, что добавлять атрибут в класс. Обычно методы работают, сохраняя функции в классе как атрибуты, а затем создавая объекты метода , поскольку эти функции извлекаются как атрибуты из класса (создание несвязанных методов, которые знают только класс, к которому они принадлежат) или экземпляр (создание которые знают их экземпляр.)

Как это отличается от того, что вы делаете? Ну, вы назначаете экземпляр атрибут специфический экземпляр, а не класс. Связанный метод становится частью данных этого экземпляра:

>>> s.__dict__ 
{'GET': <bound method Sub.get of <__main__.Sub object at 0xb70896cc>>} 

Обратите внимание, как метод есть под ключ GET, но не под get. GET - атрибут экземпляра, но get - нет. Это несколько отличается несколькими способами: метод не существует в объекте класса, поэтому вы не можете сделать Sub.GET(instance) для вызова метода SubGET, хотя вы можете сделать Sub.get(instance). Во-вторых, если у вас есть подкласс Sub, который определяет свой собственный метод GETно не его собственный метод get атрибут экземпляра будет скрыть метод подкласс GET с связанного get методом из BaseClass. В-третьих, он создает круговую ссылку между связанным методом и экземпляром: связанный метод имеет ссылку на экземпляр, и экземпляр теперь сохраняет ссылку на связанный метод. Обычно методы ограничения не сохраняются на экземпляре частично, чтобы избежать этого. Циркулярные ссылки обычно не являются большой проблемой, потому что в настоящее время у нас есть модуль cyclic-gc (gc), который заботится о них, но он не всегда может очищать эталонные циклы (например, когда ваш класс также имеет метод __del__.) И, наконец, хранение объектов связанных объектов обычно делает ваши экземпляры неэриализуемыми: большинство сериализаторов (например, 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 и более поздних), но это означало бы, что требовать, чтобы декоратор класса на каждом подклассе декораторов базового класса не был унаследован.

2

Его, потому что класс Sub не было начато ранее, сделайте это в его случае как

>>> s=Sub() 
>>> dir(s) 
['GET', '__doc__', '__init__', '__module__', 'get'] 
>>> 
-1

Создать производный конструктор в производном классе, который устанавливает атрибут.

+0

Тогда я бы повторил. Но он работает после инстанцирования, как показал «S.Mark». – deamon

Смежные вопросы