首页 > 编程问答 >Python 中 __get__ 方法的内部原理

Python 中 __get__ 方法的内部原理

时间:2024-07-25 08:23:58浏览次数:7  
标签:python descriptor

我正在摆弄描述符,结果碰壁了。我以为我可以像使用任何其他方法一样直接调用它,但显然,它似乎不一致或者我遗漏了一些东西。

假设我有一个用作描述符的坐标类:| ||还有一个 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 是因为没有在一个 实例 上访问该描述符,而是在 本身上访问它。

让我们来分解一下:

  1. point1 = Point(12, 5) :这将创建一个 Point 类的实例 point1 ,并调用 Coordinate 描述符的 __set__ 方法为 x y 属性设置值。

  2. print(point1.x) :这将访问 point1 实例的 x 属性。由于 x 是一个描述符,因此 Python 会调用 Coordinate.__get__(point1, Point) 方法。在这种情况下, instance point1 owner Point 类。 __get__ 方法从 point1 实例的 __dict__ 中检索值并返回。

  3. 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 作为返回值,或处理选择引发的任何异常。

标签:python,descriptor
From: 78791010

相关文章