描述符类如下:
class Des(object):
def __get__(self, instance, owner): ...
def __set__(self, instance, value): ...
def __delete__(self, instance): ...
class Sub(object):
attr = Des()
X = sub()
问题
我没有看到owner
存在的意义,我该如何使用它?
要使attr为只读,我们不应省略__set__
,而是定义它以捕获赋值并引发异常。因此X.attr = 123
会失败,但__set__
的参数不包含owner
,这意味着我仍然可以Sub.attr = 123
,对吗?
答案 0 :(得分: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)
答案 1 :(得分: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)
答案 2 :(得分:1)
我遇到类似的困惑时遇到了这个问题,在我自己回答之后,在这里报告我的研究结果似乎是值得谨慎的。
正如ThiefMaster已经指出的那样,“owner”参数可以构建像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')
正如我们已经建立的那样,owner
函数的__get__
参数意味着当您在类级别访问该属性时,__get__
函数会拦截getattr调用。碰巧的是,property
的代码在类级别访问时只是returns the property itself,但它可以做任何事情(比如返回一些静态值)。
现在,想象如果__set__
和__del__
以相同的方式工作会发生什么。除了实例级别之外,__set__
和__del__
方法将拦截类级别的所有setattr
和delattr
调用。
因此,这意味着MyClass
的“count”属性实际上是不可修改的。如果您习惯于使用静态编译语言(如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能力)。
答案 3 :(得分: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
答案 4 :(得分:0)
所有者只是实例的类,为方便起见而提供。您始终可以从实例计算它:
owner = instance.__class__
答案 5 :(得分:0)
__set__
方法应该更改实例上的属性。但是,如果您想要更改所有实例共享的属性并因此存在于类中,例如,是一个类属性,该怎么办?只有在您有权访问类时才能执行此操作,因此可以访问所有者参数。
是的,如果通过类分配属性,则可以覆盖属性/描述符。这是设计,因为Python是一种动态语言。
希望能回答这个问题,尽管很久以前就被问过了。