Я не буду вдаваться в целом протокола Descriptor. Я не полностью понимаю это; на самом деле, вы напомнили мне, что мне нужно бросить ленивый и действительно погрузиться в него. В то же время я скажу это:
Что я знаю do понять, что дескрипторы будут работать только на их экземпляры. Теперь вы, наверное, уже знали об этом, поэтому вы задаетесь вопросом, есть ли возможность взломать это ограничение.
Если у вас есть малейшее знакомство с метаклассами, вы узнаете, что классы также являются экземплярами. Классы могут быть экземплярами класса, который также может быть экземпляром и т. Д. Это здорово, потому что вы просите будет выглядеть примерно так:
class Descriptor(object):
def __get__(self, obj, cls):
print('__get__')
def __set__(self, obj, value):
print('__set__')
class MetaClass(type):
descriptor = Descriptor()
class Class(object):
__metaclass__ = MetaClass
# This will work fine when you do Class.descriptor, as you asked
# but it will raise an AttributeError if you do
# a = Class()
# a.descriptor
# Read on for the full explanation...
descriptor
переменных, определенные в MetaClass
виден только Class
класса. Любые экземпляры Class
, которые попытаются позвонить ему, предоставят вам AttributeError
. Это связано с тем, что экземпляры классов при поиске атрибута ищут свой собственный __dict__
перед поиском класса __dict__
, но он не будет искать до __metaclass__
класса. Теперь, если вы хотите иметь оба варианта и использовать то же имя переменной как для класса и его экземпляры (хотя я бы не рекомендовал его, как было бы пригласить к путанице), вы можете сделать это:
class Descriptor(object):
def __get__(self, obj, cls):
print('__get__')
def __set__(self, obj, value):
print('__set__')
class MetaClass(type):
descriptor = Descriptor()
class Class(object):
__metaclass__ = MetaClass
descriptor = Descriptor()
На данный момент вам может быть интересно: , если экземпляр ищет свой собственный __dict__
перед поиском его класса, как получается, что вызов «Class.descriptor
» не будет выбирать тот же дескриптор, который использует «a.descriptor
» (который, как вы видели, не будет работать должным образом), если Class.descriptor
по существу является его собственной переменной экземпляра (из метакласса POV)?
Ответ заключается в том дескрипторы данных (дескрипторы, которые имеют как __get__
и __set__
определены), в отличие от дескрипторов не-данных (которые только определенных __get__
), имеют приоритет над переменными экземпляра. Другими словами, переменная descriptor
в MetaClass
будет равна Class
, потому что она имеет приоритет над Class
собственной переменной descriptor
. То же самое относится к экземпляру Class
, который автоматически подбирает переменную descriptor
, определенную в Class
.
Надеюсь, я вас не смутил. Этот материал легко забыть, я думаю, вдвойне, потому что это не очень распространено и не нужно понимать этот уровень магии большую часть времени. Я должен был освежить эту память! Хороший вопрос :)
Фантастический, только то, что мне нужно =) – Rebs
Чтобы уточнить: при разрешении 'a.descriptor' язык не выполняет эквивалент' getattr (type (a), 'descriptor') ', которые могут быть перехвачены метаклассом. Вместо этого он «type (a) .__ dict __ ['descriptor']'. (Источник: https://docs.python.org/2/reference/datamodel.html#invoking-descriptors и 'PyObject_GenericSetAttr' в https://docs.python.org/2/c-api/object.html подтверждает что это также относится к '__set__'). – mhsmith