Python描述符`
描述符类如下所示:Python描述符`,python,descriptor,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() 问题 我看不出所有者存在的意义,我该如何使用它 要使attr为只读,我们不应该忽略\uu
class Des(object):
def __get__(self, instance, owner): ...
def __set__(self, instance, value): ...
def __delete__(self, instance): ...
class Sub(object):
attr = Des()
X = sub()
问题
所有者存在的意义,我该如何使用它
\uuuu set\uuuu
,而是定义它以捕获赋值并引发异常。所以X.attr=123
将失败,但是\uuuuuuuuuuuuuuuuuuuuuuuuu
的参数不包含owner
,这意味着我仍然可以执行Sub.attr=123
,对吗owner
始终是owner类,而instance
是通过其访问属性的实例,或者在通过owner访问属性时为None
使用owner
的情况是创建一个classproperty:
我遇到这个问题时也有类似的困惑,在我自己回答了这个问题之后,在这里报告我的发现似乎是明智的 正如ThiefMaster已经指出的,“owner”参数可以进行类似
类属性
的构造。有时,您希望类将方法屏蔽为非方法属性,使用owner
参数可以使用普通描述符实现这一点
但这只是问题的一半。关于“只读”问题,我发现:
我首先在这里找到了答案:。一开始我不明白,我花了大约五分钟的时间才把它团团转。最后说服我的是一个例子
考虑最常见的描述符:属性
。让我们使用一个简单的示例类,它有一个属性计数,它是变量计数被访问的次数
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')
正如我们已经建立的,\uuuu get\uuu
函数的所有者
参数意味着当您在类级别访问属性时,\uu get\uu
函数会截取getattr调用。碰巧,属性
的代码在类级别访问时很简单,但它可以做任何事情(比如返回一些静态值)
现在,想象一下如果\uuuu set\uuu
和\uu del\uuu
以同样的方式工作会发生什么。\uuuu set\uuuuu
和\uuuu del\uuuu
方法将拦截除实例级别之外的类级别的所有setattr
和delattr
调用
因此,这意味着MyClass
的“count”属性实际上是不可修改的。如果您习惯于使用静态编译语言(如Java)进行编程,这似乎不是很有趣,因为您无法修改应用程序代码中的类。但在Python中,您可以。类被视为对象,您可以动态分配它们的任何属性。例如,假设MyClass
是第三方模块的一部分,MyClass
几乎完全适合我们的应用程序(假设除了count
的代码之外,还有其他代码),只是我们希望count方法的工作方式有所不同。相反,对于每个实例,我们希望它总是返回10。我们可以做到以下几点:
>>> MyClass.count = 10
>>> myinstance = MyClass()
>>> myinstance.count
10
如果\uuuu set\uuuu
截获了对setattr(MyClass,'count')
的调用,那么就没有办法实际更改MyClass。相反,setcount
的代码会截取它,并且不能对它做任何事情。唯一的解决方案是编辑MyClass的源代码。(我甚至不确定是否可以在子类中覆盖它,因为我认为在子类中定义它仍然会调用setattr
代码。但我不确定,因为我们已经在处理一个反事实,所以我真的没有办法测试它。)
现在,您可能会说,“这正是我想要的!我故意不想让我的用户重新分配我的类的属性!”对此,我只能说,您想要的是不可能使用幼稚的描述符,我将向您介绍上面的推理。允许重新分配类属性更符合当前的Python习惯用法
如果你真的,真的想创建一个只读类属性,我不认为你能告诉你怎么做。但是如果有解决方案,可能需要使用元类,或者创建元类的
属性
,或者为setattr和delattr修改元类的代码。但这是一种很深的魔法,远远超出了这个答案的范围(以及我自己在Python方面的能力)。所有者只是实例的类,是为了方便而提供的。您始终可以从实例计算它:
owner = instance.__class__
您可以尝试以下示例:
# 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)
就只读属性而言(见上面的讨论),下面的示例显示了它是如何完成的:
############################################################
#
# 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
\uuuuu set\uuuu
方法应该更改实例的属性。但是,如果您想更改一个由所有实例共享并因此存在于类中的属性(例如,是类属性),该怎么办?只有在您有权访问该类(因此有owner参数)时才能执行此操作希望这能回答这个问题,尽管这个问题很久以前就被问到了。是的,我知道
所有者是什么,但是我们如何使用这个所有者
?我看不出所有者存在的意义。我想你没有回答我的第二个问题。我的猜测是,描述符不应该写在类级别,而应该写在实例级别。事实上,我也这么认为,但找不到具体的证据。只有在实例上访问描述符时,这才是真的。如果在类上访问它,实例
将是无
和所有者
wi
############################################################
#
# 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