【问题标题】:Why does the 'is' operator say these methods aren't the same? [duplicate]为什么'is'运算符说这些方法不一样? [复制]
【发布时间】:2015-01-20 01:49:53
【问题描述】:

考虑这段代码:

class Person(object):
   def sayHello(self):
       return 'Hello'

print(Person().sayHello is Person().sayHello)

我希望它显示 True。为什么显示为 False?

【问题讨论】:

  • @Test 为什么你希望你的表达式为真?您一定希望这两个子表达式是同一个对象。是什么让您得出这个结论?
  • @quamrana - 因为sayHello 只定义了一次。该方法被实例化为一个绑定的方法对象并不是很明显。
  • 因为is 用于对象,而不是方法
  • 方法是对象。但是,当您访问绑定函数时,您不会获得对最初定义函数的引用;每次访问该函数时,您都会获得一个新创建的方法对象。看到这个答案:stackoverflow.com/questions/15977808/…

标签: python class python-3.x object identity


【解决方案1】:

方法在运行时绑定到实例。当您运行以下代码时:

print(Person().sayHello is Person().sayHello)

您创建了两个实例,每次都有不同的内存地址。

>>> Person().sayHello
<bound method Person.sayHello of <__main__.Person object at 0x7fbe90640410>>
>>> Person().sayHello
<bound method Person.sayHello of <__main__.Person object at 0x7fbe90640490>>

注意:我们在 Python 中只有运行时;没有单独的编译时间这样的东西

【讨论】:

  • @user2357112 是的,当然。
  • 在 Python 中一个单独的编译时间。例如,名称范围是在编译时确定的。并且各种文字被转换为常量,并且在编译时应用了窥孔优化。不适用于这里,但不要声称它不存在。
  • 是的,但是像peephole optimisations 这样的东西完全是关于编译时间的!实际上All we have in Python 是关于对象的!和解释器范围内的过程!
【解决方案2】:

它们是同一类的两个不同实例。 sayHello 函数是绑定方法

也就是说,如果你有一个类实例:

p = Person()

然后你在它上面查找一个属性:

p.sayHello

然后Python首先查看实例的实际属性,如果没有找到该属性,它会查看类。 如果它找到同名的类方法,它将把它变成一个绑定方法,绑定到这个实例。这就是导致对象实例作为第一个参数 (self) 传递给 sayHello 的魔力。

所以Person().sayHello is Person().sayHello 创建了两个实例,基于类上定义的相同方法创建了两个不同的绑定方法,因此is 返回False,因为它们是不同的方法。

【讨论】:

  • 即使在同一个实例上,每次访问它们时都会重新创建方法。这里有两个传出实例并不重要。
【解决方案3】:

我将假设您是有意比较方法对象本身,而 不是您真的想比较输出字符串,只是忘记在 sayHello 之后放置 ()

试试这个实验:

a = Person()
b = Person()

a.sayHello
b.sayHello

您会看到a.sayHello 显示为类似

<bound method Person.sayHello of <__main__.Person instance at 0x102cc8ef0>>

...而b.sayHello 显示类似,但父实例指针不同:

<bound method Person.sayHello of <__main__.Person instance at 0x102d31908>>

Person 的一个实例的绑定方法本身与来自不同Person 实例的同名绑定方法不同 实例(方法的)。您可以使用id(a.sayHello)id(b.sayHello) 确认这一点,它们返回两个各自绑定方法的身份哈希——它们会有所不同。由于您的代码 Person().sayHello is Person().sayHello 创建两个不同的 Person 实例,情况与我的命名实例示例 ab 相同。

【讨论】:

  • 有不同的实例并不重要。即使只有一个实例,如果您访问该方法两次,您也会得到两个不同的方法对象。
  • 好点。 getattr 隐含在 a.sayHello 中的操作是每次您执行此操作时创建一个新的绑定方法实例的原因。对我来说,这被以下事实所掩盖(可能取决于平台/实现)如果您说两次id(a.sayHello),您将获得两次相同的 ID 号。但是如果你不让第一个方法实例超出范围,你会得到不同的 id 编号。所以as1 = a.sayHello; as2 = a.sayHello; print( [id(as1), id(as2) ]) 给了我不同的数字,而print([ id(a.sayHello),id(a.sayHello) ]) 给了我相同的数字。
  • 如果对象已被删除,id() 值可以重复使用。始终先创建对对象的引用,然后进行测试。
【解决方案4】:

如果你打电话给sayHello,那就是True

print(Person().sayHello() is Person().sayHello())

在您的代码中,您实际上是在比较对象上的方法并检查它们是否具有相同的标识(它们没有)。还要注意两者之间的区别:

"Hello" is "Hello"

"Hello" == "Hello"

首先,您正在比较对象的身份(由于字符串被重用,所以这是相同的,请多次调用id("Hello") 来查看)。在第二个中,您正在比较字符串的内容以查看它们是否相等(即具有相同的字符)。现在,相同的字符串也将具有相同的标识,但我不确定该假设是否适用于所有 Python 实现。

【讨论】:

  • 和"Hello"一样是"Hello",相信他是想比较函数而不是结果
  • @Test:在我的机器上是False...?
  • @SimeonVisser Python的一些实现会记忆短字符串
  • 这只是偶然的(优化这些的 Python 版本上的短字符串),而不是一般情况。
【解决方案5】:

如果你想要一个返回 True 的表达式,你可以试试这个:

print(Person.sayHello is Person.sayHello) 

只是为了增加混乱,执行:

>>> say = Person.sayHello
>>> say()
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    say()
TypeError: sayHello() missing 1 required positional argument: 'self'

甚至:

>>> say(say)
'Hello'
>>> say(None)
'Hello'
>>> 

这是因为:

>>> say
<function Person.sayHello at 0x02AF04B0>

say 指的是Person.sayHello,它是一个函数,可以调用,但需要一个参数,但在这种特殊情况下,参数是无关紧要的。

现在,如果你不想继续提供无用的参数,你可以自动绑定一个:

>>> p=Person()
>>> p.sayHello()
'Hello'

【讨论】:

  • 是的,但是有很多表达式打印 True,例如 print(True)。 OP 的问题是为什么方法似乎存在身份危机。
  • 这在返回未绑定方法的 Python 2 上仍然会失败。
【解决方案6】:

is 运算符意味着两个变量都指向同一个对象,而不是具有相同的值。请参阅堆栈溢出问题Understanding Python's “is” operator

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    • 2015-07-07
    • 2019-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多