Python如何比较两个对象使事情变得混乱的代码大致是这样的:

class Window(QtWidgets.QMainWindow):

   def __init__(self):
      super(Window,self).__init__()
      uic.loadUi('window.ui', self)
      self.show()

   def thread_it(self, func_to_execute):
      worker = Worker(func_to_execute)

      if func_to_execute == self.mpositioner.movetostart:
          worker.signals.progress.connect(self.create_raw_log_line)

      self.threadpool.start(worker)
      return worker

令人困惑的是,只有当行读数if func_to_execute == self.mpositioner.movetostart使用==运算符时,代码才能运行.当与is操作员一起使用时,它不起作用.在VSCode Pylint提出建议后,我try 了is方法,但Ruff Lintert并不抱怨==方法.

因此,问题是:is不起作用有什么特别的原因吗?

编辑:

我用suggested callback_test进行了测试,结果如下:

callback: <bound method MotorPositioner.movetostart of <__main__.MotorPositioner object at 0x000001B81B7C60E0>>
callback id: 1890339656832
callback_fun: <bound method MotorPositioner.movetostart of <__main__.MotorPositioner object at 0x000001B81B7C60E0>>
callback_fun id: 1890339659712
Yup same function

推荐答案

这里的问题是Python中的方法如何工作.简要地说:

方法只是一个存在于类的命名空间中的函数,当它通过类的instance被访问时,返回一个bound method,它是一个可调用的,实际上是partially applies the instance to the first positional argument to the original function,即它提供self参数(这是我们通常赋予方法的第一个位置参数的名称),并将实例作为参数.

因此,请考虑:

>>> class Foo:
...     def bar(self):
...         print("barring", self)
...
>>> Foo.bar
<function Foo.bar at 0x111e16fc0>

当对类本身进行访问时,它只返回函数对象.请注意,在这里,您总是会得到same object,只是在类定义语句体中创建的那个.

>>> Foo.bar is Foo.bar # this will always be True
True

但如果我们创建一个实例,我们会得到一个不同类型的对象:

>>> foo = Foo()
>>> foo.bar
<bound method Foo.bar of <__main__.Foo object at 0x11026cad0>>
>>> type(Foo.bar), type(foo.bar)
(<class 'function'>, <class 'method'>)
>>> foo.bar()
barring <__main__.Foo object at 0x11026cad0>

再次认识到,当通过类访问时,它只是您之前定义的常规函数,它的工作方式与任何其他地方定义的函数一样,它只是存在于类命名空间中:

>>> Foo.bar(42)
baring 42
>>> def baz(self):
...     print("bazzing", self)
...
>>> baz(42)
bazzing 42

注意,创建every time you access the object through the instance绑定方法对象.因此,以下各项的计算结果始终为False:

>>> foo = Foo()
>>> foo.bar is foo.bar # will always be false
False

但是,请注意,如果绑定方法对象将same instance绑定到same function,则它们将是equal.所以:

>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.bar == foo1.bar # always True
True
>>> foo1.bar == foo2.bar # always False
False

此外,这些功能还可以进行内省:

>>> foo.bar.__self__
<__main__.Foo object at 0x11026cad0>
>>> foo.bar.__func__
<function Foo.bar at 0x110250180>

A quick gotcha:

如果您正在遵循REPL,您可能会执行类似以下操作,try 验证我所做的声明是否属实.然后你做这个合理的测试:

>>> foo = Foo()
>>> for _ in range(5):
...     print(id(foo.bar))
...
4581348032
4581348032
4581348032
4581348032
4581348032

"嗯?"你可能会问."这是怎么回事,为什么我对据称不同的绑定方法对象得到相同的ID?但当我直接判断时"

>>> foo.bar is foo.bar
False

上面发生的事情是绑定方法获得reclaimed immediately after it is called because it isn't referenced anywhere.因为它是回收的,所以它的生命周期 不会重叠,所以id并不能保证它们是不同的物体.这是因为在CPython中,id仅仅是PyObject指针的值,也就是私有托管堆上对象的内存地址.该堆将愉快地重用该内存.但如果你保留一个引用,它不会被回收,所以它被迫使用另一个引用:

>>> foo = Foo()
>>> for _ in range(5):
...     bound_method = foo.bar
...     print(id(bound_method))
...
4581907008
4581530752
4581907008
4581530752
4581907008

你可以从这个例子中看到类似的现象:

>>> id(object()) == id(object())
True
>>> object() is object()
False
>>> obj1 = object()
>>> obj2 = object()
>>> id(obj1) == id(obj2)
False

这个故事的寓意是,你应该小心使用id.


如果您确实想知道这是如何发生的,那么您需要了解描述符.这Descriptor HOWTO本书深入到了细节,值得一读.描述符被Python用来实现核心功能.

Python相关问答推荐

非常奇怪:tzLocal.get_Localzone()基于python3别名的不同输出?

Pre—Commit MyPy无法禁用非错误消息

将输入聚合到统一词典中

为什么NumPy的向量化计算在将向量存储为类属性时较慢?'

将JSON对象转换为Dataframe

当点击tkinter菜单而不是菜单选项时,如何执行命令?

SQLAlchemy bindparam在mssql上失败(但在mysql上工作)

在www.example.com中使用`package_data`包含不包含__init__. py的非Python文件

如何使regex代码只适用于空的目标单元格

try 检索blob名称列表时出现错误填充错误""

* 动态地 * 修饰Python中的递归函数

如何排除prefecture_related中查询集为空的实例?

如何防止Pandas将索引标为周期?

将一个双框爆炸到另一个双框的范围内

在numpy数组中寻找楼梯状 struct

Python 3试图访问在线程调用中实例化的类的对象

没有内置pip模块的Python3.11--S在做什么?

按条件添加小计列

为什么后跟inplace方法的`.rename(Columns={';b';:';b';},Copy=False)`没有更新原始数据帧?

如何使用Polars从AWS S3读取镶木地板文件