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相关问答推荐

数据抓取失败:寻求帮助

计算每个IP的平均值

无法连接到Keycloat服务器

计算天数

Django admin Csrf令牌未设置

Flash只从html表单中获取一个值

Maya Python脚本将纹理应用于所有对象,而不是选定对象

Numpyro AR(1)均值切换模型抽样不一致性

Odoo16:模板中使用的docs变量在哪里定义?

我对这个简单的异步者的例子有什么错误的理解吗?

判断Python操作:如何从字面上得到所有decorator ?

Python协议不兼容警告

查找查找表中存在的列值组合

PYODBC错误(SQL包含-26272个参数标记,但提供了235872个参数,HY 000)

如何通过函数的强式路径动态导入函数?

在不降低分辨率的情况下绘制一组数据点的最外轮廓

如何判断变量可调用函数的参数是否都属于某个子类?

突出显示两幅图像之间的变化或差异区域

对齐多个叠置多面Seborn CAT图

滚动加权平均(或类似)填充失踪大Pandas