我正在尝试学习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?
答案 0 :(得分:9)
好吧,这就是您尝试在Python中执行隐私保护的结果。 ;)
在类主体之外,由于name mangling,必须将属性__x
和__y
分别称为_Point__x
和_Point__y
。
如果您将两个属性更改为非混杂名称(例如_x
和_y
)或在_Point__x
中使用名称_Point__y
和draw2
,您的代码不会引发错误。
我认为您应该在使用变形名称之前先三思。编写适当的文档字符串,但不要以这种烦人的方式限制类的用户。在社区中,使用单个下划线名称是众所周知的“不要碰这个”。
您可能已经注意到,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个问题。
使用双下划线字段,使它们的名称“混杂”。换句话说,当您在类__x
中指定字段Point
时,其名称实际上是_Point__x
。重点是仅允许从同一类中访问此字段(然后它将起作用)。您有2种解决方案可供选择-可以使用__Point_x
从外部访问此字段,也可以将其重命名为单个下划线字段(_x
)。
您的重新绑定是错误的。您应该在类而不是特定对象上替换它。现在,您必须调用显式传递的self
:p1.draw(p1)
。但是,如果您愿意这样做:
Point.draw = draw2
p1.draw() # works as expected