2012-05-12 2 views
0

Класс дескриптора следующим образом:Python `descriptor`

class Des(object): 
    def __get__(self, instance, owner): ... 
    def __set__(self, instance, value): ... 
    def __delete__(self, instance): ... 

class Sub(object): 
    attr = Des() 

X = sub() 

Вопрос

  1. Я не вижу смысла существования owner, как я могу его использовать?

  2. Чтобы сделать attr только для чтения, мы не должны опускать __set__, но определите его, чтобы уловить задания и создать исключение. Таким образом, X.attr = 123 не удастся, но аргументы __set__ не содержат owner, что означает, что я все еще могу сделать Sub.attr = 123, правильно?

ответ

4

См http://docs.python.org/reference/datamodel.html#implementing-descriptors:

owner всегда класс владельца, в то время как instance является экземпляр, что атрибут был доступен через, или Ни когда атрибут доступен через владельца

В случае, если вы использовали бы owner, было бы создано classproperty:

class _ContentQueryProperty(object): 
    def __get__(self, inst, cls): 
     return Content.query.filter_by(type=cls.TYPE) 
+0

Да, я знаю, что такое «владелец», но как мы можем использовать этот «владелец»? Я не вижу смысла существования «владельца». И я не думаю, что ты ответил на мой второй вопрос. – Alcott

+0

Это один, поддержал. Итак, как насчет проблемы ** для чтения **? – Alcott

+0

Я предполагаю, что дескрипторы не предназначены для написания на уровне класса, а только на уровне экземпляра. – ThiefMaster

1

Я столкнулся с этим вопросом с подобной путаницей, и после того, как я ответил сам за себя, было разумно сообщить о моих выводах здесь для процветания.

Как уже указывал ThiefMaster, параметр «владелец» делает возможными конструкции, такие как classproperty. Иногда вы хотите, чтобы классы имели маскированные маски как атрибуты не-метода, а использование параметра owner позволяет вам делать это с помощью обычных дескрипторов.

Но это только половина вопроса. Что касается вопроса «только для чтения», вот что я нашел:

Впервые я нашел ответ здесь: http://martyalchin.com/2007/nov/23/python-descriptors-part-1-of-2/. Сначала я этого не понимал, и мне потребовалось около пяти минут, чтобы обвести вокруг себя голову. Что, наконец, убедило меня, придумал пример.

Рассмотрите наиболее распространенный дескриптор: property. Давайте используем тривиальный примерный класс со счетчиком свойств, который представляет собой число обращений к счету переменных.

class MyClass(object): 
    def __init__(self): 
     self._count = 0 
    @property 
    def count(self): 
     tmp = self._count 
     self._count += 1 
     return tmp 
    @count.setter 
    def setcount(self): 
     raise AttributeError('read-only attribute') 
    @count.deleter 
    def delcount(self): 
     raise AttributeError('read-only attribute') 

Как мы уже установили, параметр функции __get__owner означает, что при обращении к атрибуту на уровне класса, функция __get__ перехватывает вызов GetAttr. Как это бывает, код для property просто returns the property itself при доступе на уровне класса, но он мог бы что угодно (например, вернуть статическое значение).

Теперь представьте, что произойдет, если __set__ и __del__ работают одинаково. Методы __set__ и __del__ будут перехватывать все вызовы setattr и delattr на уровне класса, в дополнение к уровню экземпляра.

Как следствие, это означает, что атрибут «count» MyClass фактически равен немодифицируемым. Если вы привыкли программировать на статических, скомпилированных языках, таких как Java, это не кажется очень интересным, поскольку вы не можете изменять классы в коде приложения. Но на Python вы можете. Классы считаются объектами, и вы можете динамически назначать любые их атрибуты. Например, допустим, что MyClass является частью стороннего модуля, а MyClass почти полностью подходит для нашего приложения (предположим, что там есть другой код, кроме кода для count), за исключением того, что мы хотели, чтобы метод подсчета работал несколько иначе. Вместо этого мы хотим, чтобы он всегда возвращал 10 для каждого отдельного экземпляра. Мы могли бы сделать следующее:

>>> MyClass.count = 10 
>>> myinstance = MyClass() 
>>> myinstance.count 
10 

__set__ Если перехвачен вызов setattr(MyClass, 'count'), то не было бы никакой возможности реально изменить MyClass. Вместо этого код для setcount перехватит его и ничего не сможет с ним поделать. Единственным решением было бы изменить исходный код MyClass. (Я даже не уверен, что вы можете перезаписать его в подклассе, потому что я думаю, что определение его в подклассе все равно вызовет код setattr. Но я не уверен, и поскольку мы уже имеем дело с контрфактуальным здесь, я на самом деле у меня нет способа проверить его.)

Теперь вы можете сказать: «Это именно то, что я хочу!» намеренно не хотел, чтобы мой пользователь переназначил атрибуты моего класса! » К этому я могу сказать только то, что вы не хотели использовать наивные дескрипторы, и я бы направил вас к рассуждениям выше. Разрешить переназначение атрибутов класса намного больше соответствует текущим идиомам Python.

Если вы действительно, ДЕЙСТВИТЕЛЬНО хотите, чтобы атрибут класса только для чтения, я не думаю, что мог бы сказать вам, как. Но если есть решение, это, вероятно, связаны с использованием метаклассов и либо создание property из метакласса или модификации кода Метакласс для SetAttr и delattr. Но это Deep Magic, и далеко не полный объем этого ответа (и мои собственные способности с Python).

0

Владелец является только классом экземпляра и предназначен для удобства. Вы всегда можете вычислить его из экземпляра:

owner = instance.__class__ 
+0

Это справедливо только в том случае, если вы обращаетесь к дескриптору экземпляра. Если вы получите доступ к нему в классе, 'instance' будет' None', а 'owner' будет классом, к которому вы обращались в нем (не' NoneType', что подразумевает ваш код). Теперь, для большинства типов дескрипторов, которые не заботятся о доступе к классу, это, вероятно, не имеет значения, но если вам все равно, это важно. – Blckknght

+0

Я согласен - если вы получите доступ к дескриптору через r-значение. Однако, если вы попробуете доступ к l-значению, кажется, что прямой доступ к словарю пытается, и это перезапишет дескриптор. Даже при доступе к r-значению это спорный вопрос о том, что вы вернете - вы можете создать исключение - если вы не сохраните атрибут класса под другим именем. – resigned

+0

Как говорится в документах: – resigned

1

Вы можете поэкспериментировать с этим примером:

# the descriptor protocol defines 3 methods: 
# __get__() 
# __set__() 
# __delete__() 

# any class implementing any of the above methods is a descriptor 
# as in this class 
class Trace(object): 
    def __init__(self, name): 
     self.name = name 

    def __get__(self, obj, objtype): 
     print "GET:" + self.name + " = " + str(obj.__dict__[self.name]) 
     return obj.__dict__[self.name] 

    def __set__(self, obj, value): 
     obj.__dict__[self.name] = value 
     print "SET:" + self.name + " = " + str(obj.__dict__[self.name]) 

# define the attributes of your class (must derive from object) 
# to be references to instances of a descriptor 

class Point(object): 
# NOTES: 
# 1. descriptor invoked by dotted attribute access: A.x or a.x 
# 2. descripor reference must be stored in the class dict, not the instance dict 
# 3. descriptor not invoked by dictionary access: Point.__dict__['x'] 

    x = Trace("x") 
    y = Trace("y") 

    def __init__(self, x0, y0): 
     self.x = x0 
     self.y = y0 

    def moveBy(self, dx, dy): 
     self.x = self.x + dx  # attribute access does trigger descriptor 
     self.y = self.y + dy 


# trace all getters and setters  
p1 = Point(15, 25) 
p1.x = 20 
p1.y = 35 
result = p1.x 
p2 = Point(16, 26) 
p2.x = 30 
p2.moveBy(1, 1) 
1

Насколько только для чтения свойства касаются (см обсуждение выше), в следующем примере показано, как его сделать :

############################################################ 
# 
# descriptors 
# 
############################################################ 

# define a class where methods are invoked through properties 
class Point(object): 
    def getX(self): 
    print "getting x" 
    return self._x 

    def setX(self, value): 
    print "setting x" 
    self._x = value 

    def delX(self): 
    print "deleting x" 
    del self._x 

    x = property(getX, setX, delX) 

p = Point() 
p.x = 55 # calls setX 
a = p.x  # calls getX 
del p.x  # calls delX 

# using property decorator (read only attributes) 
class Foo(object): 
    def __init__(self, x0, y0): 
    self.__dict__["myX"] = x0 
    self.__dict__["myY"] = y0 

    @property 
    def x(self): 
    return self.myX 

f = Foo(4,6) 
print f.x 
try: 
    f.x = 77  # fails: f.x is read-only 
except Exception,e: 
    print e 
0
  1. __set__ метод должен изменять атрибуты в экземпляре. Но что, если вы хотите изменить атрибут, который является общим для всех экземпляров и, следовательно, живет в классе, например, является атрибутом класса? Это можно сделать только в том случае, если у вас есть доступ к классу, следовательно, аргумент владельца.

  2. Да, вы можете переписать свойство/дескриптор, если вы назначаете атрибут через класс. Это по дизайну, поскольку Python - динамический язык.

Надеюсь, что ответит на вопрос, хотя он был задан давно.

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