我正在摆弄描述符,结果碰壁了。我以为我可以像使用任何其他方法一样直接调用它,但显然,它似乎不一致或者我遗漏了一些东西。
假设我有一个用作描述符的坐标类:| ||还有一个 Point 类,它有 2 个坐标属性:
class Coordinate:
def __set_name__(self, owner, name):
self._name = name
def __get__(self, instance, owner):
return instance.__dict__[self._name]
def __set__(self, instance, value):
try:
instance.__dict__[self._name] = float(value)
except ValueError:
raise ValueError(f'"{self.__set_name__}" must be a number') from None
假设我在我的主代码上创建了一个像这样的新点:
class Point:
x = Coordinate()
y = Coordinate()
def __init__(self, x, y):
self.x = x
self.y = y
我知道如果我尝试这样做:
point1 = Point(12,5)
我将在控制台中得到这个:
print(point1.x)
我知道如果我尝试这个,它将失败:
12.0
My
print(point1.x.__get__(Point))
get 方法将使用如下参数调用: self
- :将是 point1.x,坐标实例。 实例
- :将是我的 point1,Point 实例。 所有者
- :将是 Point 类。 所有这些都是因为参数的顺序。因为我有:
x
x.__get__()
将是我的第一个论点。
然后,因为我
x
point1.x.__get()
将是我的第一个参数,而
point1
将是我的第二个参数。
并且让
Point
类在括号内传递,完成什么我想要,但它仍然不起作用,因为即使
get
方法返回
,Python 现在也会尝试调用由返回的 float (12.0) 中的
12.0
get
方法我的手册
get
调用,这将引发异常。
所有这些对我来说都是有意义的。什么
'float' object has no attribute '__get__'
没有 意义的是,当我尝试做这样的事情时: 而不是像这样填充我的
print(Point.x.__get__(Point))
get 方法参数: self
- :将是 Point.x,即在 Point 类中声明的坐标实例。 实例
- :将是对我的 Point 类本身的引用。 所有者
- :也将是对 Point 类的引用 它将
实例 参数填充为 None。 为什么会发生这种情况?如果所有其他参数都正确传递,为什么它不填充对 Point 的引用? (self 和owner 参数都有预期值)。
Why does it happen? Why doesn't it populate with a reference to Point, if all the other arguments are being passed correctly? (Both the self and owner arguments have the expected values).
的理解几乎正确。当调用
Point.x.__get__(Point)
时,
__get__
方法中的
instance
参数被设置为
None
是因为没有在一个
实例
上访问该描述符,而是在
类
本身上访问它。
让我们来分解一下:
-
point1 = Point(12, 5)
:这将创建一个Point
类的实例point1
,并调用Coordinate
描述符的__set__
方法为x
和y
属性设置值。 -
print(point1.x)
:这将访问point1
实例的x
属性。由于x
是一个描述符,因此 Python 会调用Coordinate.__get__(point1, Point)
方法。在这种情况下,instance
是point1
,owner
是Point
类。__get__
方法从point1
实例的__dict__
中检索值并返回。 -
print(Point.x.__get__(Point))
:这将直接在Point
类 上访问x
描述符。由于没有在一个实例上访问它,因此instance
参数被设置为None
。本质上,是在询问 "如果有一个Point
实例,那么它的x
属性会是什么?",但还没有创建该实例。
那么,为什么
instance
在这种情况下是
None
呢?
描述符被设计为在实例级别和类级别都可用。当在一个实例(如
point1.x
)上访问描述符时,
instance
参数指向该实例,允许检索或设置特定于该实例的值。
但是,当在一个类(如
Point.x
)上访问描述符时,
没有实例上下文
。并没有引用任何特定
Point
实例的
x
属性。在这种情况下,
instance
被设置为
None
,表示没有与调用相关的实例。
如何修复的代码?
如果想在没有
Point
实例的情况下调用
__get__
方法并获取有意义的结果,则需要修改的代码来处理
instance
为
None
的情况。例如,可以返回一个默认值或引发异常:
class Coordinate:
# ... 其他方法 ...
def __get__(self, instance, owner):
if instance is None:
return None # 或引发异常
else:
return instance.__dict__[self._name]
此更改将允许调用
print(Point.x.__get__(None, Point))
并获得
None
作为返回值,或处理选择引发的任何异常。