2014-11-21 42 views
11

考虑以下代码:'is'运算符为什么说这些方法不一样?

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

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

我希望它表现出真正的。它为什么显示错误?

+1

看到这个问题http://stackoverflow.com/a/133024/1394473 – tom 2014-11-21 20:15:32

+4

@汤姆,虽然这并不能真正解释比较实例方法 – davidism 2014-11-21 20:18:29

+0

@Test你为什么期望你的表达式为真?你必须期待这两个子表达式是同一个对象。是什么导致你这个断言? – quamrana 2014-11-21 21:20:29

回答

16

方法对在运行势必实例。当您运行以下代码时:

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都是运行时;有没有这样的事情作为一个单独的编译时间

+10

[如果你有一个实例,你会*仍然*得到'False',因为你在每次访问时都会得到一个新的方法对象。](http://stackoverflow.com/questions/15977808/why-dont-methods -have-reference-equality) – user2357112 2014-11-22 01:35:44

+0

@ user2357112是的,当然。 – Kasramvd 2014-11-22 06:08:52

+4

*在python *中是单独的编译时间。例如,名称范围在编译时确定。各种文字变成常数,并在编译时应用窥视孔优化。这里不适用,但不要求它不存在。 – 2014-11-25 08:04:23

0

这将是True如果你叫sayHello

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

在你的代码实际上是比较上的对象的方法,并检查它们是否具有相同的身份(他们没有)。另外要注意的区别:

"Hello" is "Hello" 

"Hello" == "Hello" 

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

+0

它是一样的“你好”是“你好”,我相信他想比较功能而不是结果 – Andres 2014-11-21 20:13:29

+0

@Test:在我的机器上是'False'...? – 2014-11-21 20:13:45

+3

@SimeonVisser Python的一些实现会记忆短字符串 – 2014-11-21 20:14:57

5

我会假设你是有意比较方法对象本身,而不是你真的想比较输出字符串,只是忘了()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>> 

的0一个实例的结合方法本身是一个不同实例(方法)从同一个名字的从不同的Person实例绑定的方法。你可以用id(a.sayHello)id(b.sayHello)来确认,它返回两个绑定方法的标识哈希 - 它们将会不同。由于您的代码Person().sayHello is Person().sayHello创建了两个不同的Person实例,因此情况与我的命名实例示例ab相同。

+2

没有关系,有不同的情况。即使只有一个实例,如果您访问该方法两次,您也会得到两个不同的方法对象。 – 2014-11-25 07:54:50

+0

好点。 'a.sayHello'中隐含的'getattr'操作是每次你创建一个新的绑定方法实例。对于我来说,这被一个事实掩盖了(可能取决于平台/实现),如果你说'id(a.sayHello)'两次,你会得到相同的id号码两次。但是如果你不让第一个方法实例超出范围,你会得到*不同的* id号。所以'as1 = a.sayHello; as2 = a.sayHello; print([id(as1),id(as2)])'给了我不同的数字,而'print([id(a.sayHello),id(a.sayHello)])'给了我相同的数字。 – jez 2014-11-25 19:02:28

+0

如果对象已被删除,'id()'值可以重用。总是先创建一个对象,然后再测试。 – 2014-11-25 19:25:47

6

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

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

p = Person() 

,你就可以查找一个属性:

p.sayHello 

那么Python首先着眼于实例的实际属性,如果它没有找到那里的属性,它看着这个类。 如果它找到该名称的类方法,它会将其变成绑定方法,绑定到此实例。这是导致对象实例作为第一个参数(self)传递到sayHello的魔法。

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

+2

即使在相同的实例中,每次访问它们时都会重新创建方法。无所谓这里有两个传出实例。 – 2014-11-25 07:53:39

-1

如果你想它返回一个表达式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' 
+4

是的,但有很多表达式可以打印True,就像'print(True)'。 OP的问题是为什么方法似乎有身份危机。 – tdelaney 2014-11-21 21:15:03

+1

在Python 2中,返回未绑定方法的情况下仍会失败。 – 2014-11-25 07:56:42

相关问题