0

一个描述符类如下:

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__'s arguments 不包含owner,这意味着我仍然可以这样做Sub.attr = 123,对吗?

4

6 回答 6

4

请参阅http://docs.python.org/reference/datamodel.html#implementing-descriptors

owner始终是所有者类,instance而是通过所有者访问属性的实例,或者当通过所有者访问属性时为 None

您将使用的一种情况owner是创建一个classproperty

class _ContentQueryProperty(object):
    def __get__(self, inst, cls):
        return Content.query.filter_by(type=cls.TYPE)
于 2012-05-12T09:24:15.310 回答
2

你可以试试这个例子:

# 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)
于 2013-06-08T21:41:02.550 回答
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
于 2013-06-12T12:36:54.867 回答
1

我遇到了类似的困惑这个问题,在我自己回答之后,为了繁荣,在这里报告我的发现似乎是谨慎的。

正如 ThiefMaster 已经指出的那样,“所有者”参数使诸如classproperty. 有时,您希望类将方法屏蔽为非方法属性,并且使用owner参数允许您使用普通描述符来做到这一点。

但这只是问题的一半。至于“只读”问题,这是我发现的:

我首先在这里找到了答案:http: //martyalchin.com/2007/nov/23/python-descriptors-part-1-of-2/。起初我不明白,我花了大约五分钟的时间才把头绕过去。最终说服我的是举了一个例子。

考虑最常见的描述符:property. 让我们使用一个简单的示例类,它有一个属性 count,即变量 count 被访问的次数。

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')

正如我们已经建立owner的,函数的参数__get__意味着当你在类级别访问属性时,__get__函数会拦截getattr调用。碰巧的是,当在类级别访问时,代码property只是简单地返回属性本身,但它可以做任何事情(比如返回一些静态值)。

__set__现在,想象一下如果以同样的__del__方式工作会发生什么。除了实例级别之外,and 方法将在类级别拦截__set__所有__del__setattr调用。delattr

因此,这意味着 的“计数”属性MyClass实际上是不可修改的。如果您习惯于使用 Java 等静态编译语言进行编程,这似乎不是很有趣,因为您无法修改应用程序代码中的类。但在 Python 中,你可以。类被视为对象,您可以动态分配它们的任何属性。例如,假设MyClass它是第三方模块的一部分,并且MyClass几乎完全适合我们的应用程序(假设除了 for 的代码之外还有其他代码count),只是我们希望 count 方法的工作方式有所不同。相反,我们希望它始终为每个实例返回 10。我们可以做到以下几点:

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

如果__set__截获对 的调用setattr(MyClass, 'count'),则无法实际更改 MyClass. 相反, for 的代码setcount会拦截它并且无法对它做任何事情。唯一的解决方案是编辑 MyClass 的源代码。(我什至不确定你是否可以在子类中覆盖它,因为我认为在子类中定义它仍然会调用setattr代码。但我不确定,因为我们已经在这里处理了反事实,我不真的没有办法测试它。)

现在,您可能会说,“这正是我想要的!我故意不希望我的用户重新分配我的类的属性!” 对此,我只能说你想要的东西是不可能使用幼稚的描述符的,我会指导你上面的推理。允许重新分配类属性更符合当前的 Python 习惯用法。

如果你真的,真的想创建一个只读的类属性,我想我不会告诉你怎么做。但如果有解决方案,它可能涉及使用元类并创建property或修改元类的setattrdelattr代码。但这是深度魔法,远远超出了这个答案的范围(以及我自己对 Python 的能力)。

于 2012-12-14T19:32:17.050 回答
0

所有者只是实例的类,是为了方便而提供的。您始终可以从实例计算它:

owner = instance.__class__
于 2013-06-08T21:31:57.047 回答
0
  1. __set__方法应该更改实例的属性。但是,如果您想更改所有实例共享并因此存在于类中的属性,例如,是类属性怎么办?这只能在您有权访问该类时才能完成,因此是 owner 参数。

  2. 是的,如果您通过类分配给属性,则可以覆盖属性/描述符。这是设计使然,因为 Python 是一种动态语言。

希望能回答这个问题,尽管很久以前就有人问过了。

于 2017-03-20T11:54:25.140 回答