我理解 Everything is a Object 背后的主要理论,但我真的不明白它是如何在幕后实现的。
所以:foo(4)
与foo.__call__(4)
相同。但是什么阻止我做foo.__call__.__call__(4)
?
foo
是一个函数,foo.__call__...
是函数的所有方法包装器,但是当我调用一个函数时,甚至调用了哪些函数?
我的foo
函数有很多属性,每个对象都存储了很多属性,那么它如何不占用无限的内存呢?
sys.getsizeof('a')
生成22
,对于一个字符来说似乎相当大,但是因为它引用了71个属性而非常小。
我想我要问的是,如果我想实现一个天真的python版本(我不是,但它似乎是最好的问题)我将如何实现它?
修改1
我对builtins进行了一些了解,并意识到它们引用了相同的属性(id('a'.upper) == id('b'.upper)
)。这让我问它是如何知道它正在访问的对象?
修改2 作为pts points out 'a'.upper is not 'b'.upper
,以便清除它。
我看过source for IronPython因为我认为它会帮助我理解,但它让我更加困惑。
答案 0 :(得分:10)
我认为你感到困惑的是,尽管所有Python的变量都可能是对象,并且这些变量的所有属性都可能是对象,但是存在限制。我的意思是,对于正常的类,结构通常是:
myclass -> classobj -> type
如果你在控制台中尝试这个,你可以看到:
>> class A:
.. pass
..
>> print type(A)
<type 'classobj'>
>> print type(type(A))
<type 'type'>
但如果您尝试深入type
,则只需获得type
。 type
是Python中所有对象的基础对象。
>> print type(type(type(A)))
<type 'type'>
>> print type(type(type(type(type(A)))))
<type 'type'>
您不仅获得相同的基类/对象类型,而且您为A的类实例获得该类型的相同实例,因此尽管您可以对函数type
进行无限递归(即。type(type(type(type(... ))))
),你不会去任何地方(即没有探索无限记忆的无底洞。)
>> id(type(type(type(A)))
505578544
>> id(type(type(type(type(A))))
505578544
同样的原则适用于Python中的所有对象以及Python中对象的所有属性,因为它们也是对象,但它们都是有限的。
回答你之前的一个问题,
的'method-wrapper'实例相同
foo.__call__
与'{1}}
所以没有什么可以阻止你用foo.__call__.__call__
......
Python对你起了一个作用,因为foo.__call__.__call__.__call__
为'method-wrapper'提供了一个实例,在'method-wrapper'类中有一个函数/变量,也称为foo.__call__
到该类的同一个实例。
答案 1 :(得分:2)
您认为如果所有内容都是对象,则foo(4)
必须自动转换为foo.__call__(4)
是错误的。
Python首先可以检查对象foo
是否是一个函数,如果是,则调用它,然后执行其他操作(比如查找__call__
属性)。
答案 2 :(得分:1)
您可以将“对象”视为指向其数据成员字典的指针,并指向其类型的数据成员字典(通常包括该类型的方法)。这些通常是有限空间,类型的所有对象共享类型的字典。 (这是一种过度简化,但可能足以理解为什么它不是无限的记忆)。
答案 3 :(得分:1)
id
异常是由早期引用计数引起的:Python内存管理器过早释放'a'.upper
,然后'b'.upper
被分配到相同的内存地址。
早免费:
>>> id('a'.upper), id('b'.upper)
(3073677900L, 3073677900L)
没有早期免费:
>>> x, y = 'a'.upper, 'b'.upper
>>> id(x), id(y)
(3073678252L, 3073677900L)
>>> x, y
(<built-in method upper of str object at 0xb7317b20>, <built-in method upper of str object at 0xb7317b40>)
参考比较:
>>> 'a'.upper is 'b'.upper
False