Python:将绑定方法更改为另一种方法

时间:2018-10-14 06:12:18

标签: python class object

我正在尝试学习python中的绑定方法,并实现了以下代码:

class Point:
    def __init__(self, x,y):
        self.__x=x
        self.__y=y

    def draw(self):
        print(self.__x, self.__y)

def draw2(self):
    print("x",self.__x, "y", self.__y)

p1=Point(1,2)
p2=Point(3,4)
p1.draw()
p2.draw()
p1.draw=draw2
p1.draw(p1)

运行此代码时,将产生以下输出:

1 2
3 4
Traceback (most recent call last):
  File "main.py", line 17, in <module>
    p1.draw(p1)
  File "main.py", line 10, in draw2
    print("x",self.__x, "y", self.__y)
AttributeError: 'Point' object has no attribute '__x'

为什么我不能在更改p1.draw()后将其指向draw2?

4 个答案:

答案 0 :(得分:9)

好吧,这就是您尝试在Python中执行隐私保护的结果。 ;)

在类主体之外,由于name mangling,必须将属性__x__y分别称为_Point__x_Point__y

如果您将两个属性更改为非混杂名称(例如_x_y)或在_Point__x中使用名称_Point__ydraw2,您的代码不会引发错误。

我认为您应该在使用变形名称之前先三思。编写适当的文档字符串,但不要以这种烦人的方式限制类的用户。在社区中,使用单个下划线名称是众所周知的“不要碰这个”。

您可能已经注意到,p1.draw在猴子补丁之后的行为有所不同,因为draw2不是实例p1的绑定方法,因此您需要传递{{1 }}作为参数。我建议您在重新分配名称p1之前,通过利用函数的descriptor protocol将实例p1绑定到draw2

将所有内容放在一起,将代码

draw

产生输出

class Point:
    def __init__(self, x,y):
        self._x=x
        self._y=y

    def draw(self):
        print(self._x, self._y)

def draw2(self):
    print("x",self._x, "y", self._y)

p1 = Point(1,2)
p2 = Point(3,4)
p1.draw()
p2.draw()
p1.draw = draw2.__get__(p1)
p1.draw()

其中1 2 3 4 x 1 y 2 产生行为类似于draw2.__get__(p1)的可调用对象,但会自动将draw2作为第一个参数传递。

答案 1 :(得分:2)

双下划线引起Python 'mangle'属性名称。它实际上将存储为_Point__x而不是__x。如果您这样更改功能,它将起作用:

def draw2(self):
    print("x",self._Point__x, "y", self._Point__y)

双下划线应该表示一个变量,该变量是该类专用的,不应在该类外部访问。 Name mangling makes it more difficult to do so accidentally

答案 2 :(得分:1)

这是因为您定义了__x,就像您看到的那样:

p1=Point(1,2)
p1.__x

它又说'Point' object has no attribute '__x'。所以这个问题不是由于您的绘图功能。这是您的属性。因此,如果您这样定义课程:

class Point:
    def __init__(self, x,y):
        self.x=x
        self.y=y

    def draw(self):
        print(self.x, self.y)

def draw2(self):
    print("x",self.x, "y", self.x)

这将很好地工作。

另外,如果您使用拳头的实现方式,并且使用dir函数,则可以看到__x__y_Point__x_Point__y一样可访问。因此您也可以这样做:

class Point:
    def __init__(self, x,y):
        self.__x=x
        self.__y=y

    def draw(self):
        print(self.__x, self.__y)

def draw2(self):
    print("x",self._Point__x, "y", self._Point__y)
  

请注意,使用__是为了定义私有属性。

答案 3 :(得分:0)

您的代码有2个问题。

  1. 使用双下划线字段,使它们的名称“混杂”。换句话说,当您在类__x中指定字段Point时,其名称实际上是_Point__x。重点是仅允许从同一类中访问此字段(然后它将起作用)。您有2种解决方案可供选择-可以使用__Point_x从外部访问此字段,也可以将其重命名为单个下划线字段(_x)。

  2. 您的重新绑定是错误的。您应该在类而不是特定对象上替换它。现在,您必须调用显式传递的selfp1.draw(p1)。但是,如果您愿意这样做:

    Point.draw = draw2
    p1.draw()  # works as expected