2010-07-18 1 views
1

Я хотел бы иметь некоторые дескрипторы данных как часть класса. Это означает, что я бы хотел, чтобы атрибуты класса фактически были свойствами, доступ к которым обрабатывается методами класса.Реализация дескрипторов классов путем подкласса класса `type`

Кажется, что Python напрямую не поддерживает это, но может быть реализовано путем подкласса класса type. Поэтому добавление свойства в подкласс type приведет к тому, что его экземпляры будут иметь дескрипторы для этого свойства. Его экземплярами являются классы. Таким образом, дескрипторы классов.

Возможно ли это? Есть ли какие-то ошибки, за которые я должен следить?

+1

Эта статья может быть полезна: http://stackoverflow.com/questions/128573 – Philipp

+0

Действительно, это почти дубликат, за исключением того, что я явно спросил о подклассификации 'type', тогда как это просто предлагается в одном из ответов для этого вопроса. – intuited

ответ

1

Это соглашение (обычно), для дескриптора при доступе к классу для возврата самого объекта дескриптора. Это то, что делает property; если вы получаете доступ к объекту свойства в классе, вы возвращаете объект свойства (потому что это то, что он делает __get__ метод). Но это конвенция; вам не нужно так поступать.

Так что, если вам нужно только иметь геттер дескриптора на классе, и вы не возражаете, что попытка установить перепишут дескриптор, вы можете сделать что-то подобное без программирования метаклассов:

def classproperty_getter_only(f): 
    class NonDataDescriptor(object): 
     def __get__(self, instance, icls): 
      return f(icls) 
    return NonDataDescriptor() 

class Foo(object): 

    @classproperty_getter_only 
    def flup(cls): 
     return 'hello from', cls 

print Foo.flup 
print Foo().flup 

для

('hello from', <class '__main__.Foo'>) 
('hello from', <class '__main__.Foo'>) 

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

Целесообразно? Я так не думаю. Я бы не делал то, что вы описываете случайно в производственном коде; Я бы подумал только об этом, если бы у меня была очень веская причина сделать это (и я не могу придумать такой сценарий с моей головы). Метаклассы очень мощные, но они не совсем понятны всем программистам, и их несколько сложнее рассуждать, поэтому их использование затрудняет работу с вашим кодом. Я думаю, что такого рода дизайн будет недооценивать сообщество python в целом.

+0

Отличный ответ, спасибо. Когда вы говорите * metaclass *, вы имеете в виду то, что я описывал, т.е. подклассифицирование типа? Эта терминология кажется неоднозначной, поскольку она отличается от атрибута '__metaclass__', который, как я понимаю, управляет только классом * *. Есть ли способ различать эти два понятия? Или подклассифицировать «тип» так редко, что мало нужно? – intuited

+1

Насколько я знаю, подклассификация 'type' действительно полезна, если вы используете ее как' __metaclass__'. Связь между классом и метаклассом полностью аналогична взаимосвязи между экземпляром объекта и его классом. Класс класса. Когда вы выполняете 'print MyClass', что приводит к неявной' str (MyClass) '(текст, который мы печатаем), метод, который на самом деле вызывается для создания этого текста, является' type .__ str__' (или '__str__' метод на вашем пользовательский метакласс). Настройка метакласса * обычно * делает свое дело во время создания класса, но может быть настроена другими способами. –

+0

Хорошо, я понял. Я на самом деле вызывал метакласс как с 3-аргументным вызовом 'type' для создания класса. Я просто не понял, что делает '__metaclass__'. Наверное, меня смутило утверждение документации о том, что его эффекты происходят при построении класса. Но тогда то же самое относится и к самому объявлению класса :) Еще раз спасибо. – intuited

1

Это то, что вы подразумеваете под «атрибутами класса», которые фактически являются свойствами, доступ к которым осуществляется методами класса »?

Вы можете использовать декоратор property, чтобы аксессуар отображался как фактический элемент данных. Затем вы можете использовать декоратор x.setter, чтобы сделать сеттер для этого атрибута.

Обязательно наследуйте от object, иначе это не сработает.

class Foo(object): 
    def __init__(self): 
      self._hiddenx = 3 
    @property 
    def x(self): 
      return self._hiddenx + 10 
    @x.setter 
    def x(self, value): 
      self._hiddenx = value 

p = Foo() 

p.x #13 

p.x = 4 
p.x #14 
+0

Нет, я хочу иметь возможность делать 'Foo.x' для вызова' x (cls) '. – intuited