简体   繁体   English

为什么具有相同`id`的两个函数具有不同的属性?

[英]Why can two functions with the same `id` have different attributes?

Why can two functions with the same id value have differing attributes like __doc__ or __name__ ? 为什么具有相同id值的两个函数具有不同的属性,如__doc____name__

Here's a toy example: 这是一个玩具示例:

some_dict = {}
for i in range(2):
    def fun(self, *args):
        print i
    fun.__doc__ = "I am function {}".format(i)
    fun.__name__ = "function_{}".format(i)
    some_dict["function_{}".format(i)] = fun

my_type = type("my_type", (object,), some_dict)
m = my_type()

print id(m.function_0)
print id(m.function_1)
print m.function_0.__doc__
print m.function_1.__doc__
print m.function_0.__name__
print m.function_1.__name__
print m.function_0()
print m.function_1()

Which prints: 哪个印刷品:

57386560
57386560
I am function 0
I am function 1
function_0
function_1
1 # <--- Why is it bound to the most recent value of that variable?
1

I've tried mixing in a call to copy.deepcopy (not sure if the recursive copy is needed for functions or it is overkill) but this doesn't change anything. 我已经尝试混合调用copy.deepcopy (不确定函数是否需要递归副本或者它是否过度)但这并没有改变任何东西。

You are comparing methods , and method objects are created anew each time you access one on an instance or class (via the descriptor protocol ). 您正在比较方法 ,并且每次访问实例或类(通过描述符协议 )时,方法对象都会重新创建。

Once you tested their id() you discard the method again (there are no references to it), so Python is free to reuse the id when you create another method. 一旦你测试了他们的id()你就会再次丢弃这个方法(没有引用它),所以Python在你创建另一个方法时可以自由地重用id。 You want to test the actual functions here, by using m.function_0.__func__ and m.function_1.__func__ : 您想通过使用m.function_0.__func__m.function_1.__func__来测试这里的实际功能:

>>> id(m.function_0.__func__)
4321897240
>>> id(m.function_1.__func__)
4321906032

Method objects inherit the __doc__ and __name__ attributes from the function that they wrap. 方法对象从它们包装的函数继承__doc____name__属性。 The actual underlying functions are really still different objects. 实际的底层函数实际上仍然是不同的对象。

As for the two functions returning 1 ; 至于两个函数返回1 ; both functions use i as a closure; 这两个函数都使用i作为闭包; the value for i is looked up when you call the method , not when you created the function. 调用方法时查找i的值,而不是在创建函数时查找。 See Local variables in Python nested functions . 请参阅Python嵌套函数中的局部变量

The easiest work-around is to add another scope with a factory function: 最简单的解决方法是使用工厂函数添加另一个范围:

some_dict = {}
for i in range(2):
    def create_fun(i):
        def fun(self, *args):
            print i
        fun.__doc__ = "I am function {}".format(i)
        fun.__name__ = "function_{}".format(i)
        return fun
    some_dict["function_{}".format(i)] = create_fun(i)

Per your comment on ndpu's answer, here is one way you can create the functions without needing to have an optional argument: 根据您对ndpu的答案的评论,这里有一种方法可以创建函数而无需具有可选参数:

for i in range(2):
    def funGenerator(i):
        def fun1(self, *args):
            print i
        return fun1
    fun = funGenerator(i)
    fun.__doc__ = "I am function {}".format(i)
    fun.__name__ = "function_{}".format(i)
    some_dict["function_{}".format(i)] = fun

You should save current i to make this: 你应该保存当前的i来做到这一点:

1 # <--- Why is it bound to the most recent value of that variable?
1

work, for example by setting default value to function argument: 工作,例如通过将默认值设置为函数参数:

for i in range(2):
    def fun(self, i=i, *args):
        print i
# ...

or create a closure: 或创建一个闭包:

for i in range(2):
    def f(i):
        def fun(self, *args):
            print i
        return fun
    fun = f(i)
# ...

@Martjin Pieters is perfectly correct. @Martjin Pieters是完全正确的。 To illustrate, try this modification 为了说明,请尝试此修改

some_dict = {}

for i in range(2):
    def fun(self, *args):
        print i

    fun.__doc__ = "I am function {}".format(i)
    fun.__name__ = "function_{}".format(i)
    some_dict["function_{}".format(i)] = fun
    print "id",id(fun)

my_type = type("my_type", (object,), some_dict)
m = my_type()

print id(m.function_0)
print id(m.function_1)
print m.function_0.__doc__
print m.function_1.__doc__
print m.function_0.__name__
print m.function_1.__name__
print m.function_0()
print m.function_1()

c = my_type()
print c
print id(c.function_0)

You see that the fun get's a different id each time, and is different from the final one. 你会发现每次都有一个不同的id,并且与最后一个不同。 It's the method creation logic that send's it pointing to the same location, as that's where the class's code is stored. 这是方法创建逻辑,它发送它指向相同的位置,就像存储类的代码一样。 Also, if you use the my_type as a sort of class, instances created with it have the same memory address for that function 此外,如果将my_type用作某种类,则使用它创建的实例具有该函数的相同内存地址

This code gives: 此代码给出:
id 4299601152 id 4299601152
id 4299601272 id 4299601272
4299376112 4299376112
4299376112 4299376112
I am function 0 我是功能0
I am function 1 我是职能1
function_0 function_0
function_1 function_1
1 1
None 没有
1 1
None 没有
< main .my_type object at 0x10047c350> <0x10047c350处的主要 .my_type对象>
4299376112 4299376112

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM