可以使用getattr(obj,attr)或inspect.getmembers(obj)获取对象属性,然后按名称过滤:
import inspect
class Foo(object):
def __init__(self):
self.a = 100
def method(self): pass
foo = Foo()
method_by_getattr = getattr(foo, 'method')
foo_members = inspect.getmembers(foo)
method_by_inspect = [member[1] for member in foo_members
if member[0] == "method"][0]
print (id(method_by_getattr), method_by_getattr, type(method_by_getattr))
print (id(method_by_inspect), method_by_inspect, type(method_by_inspect))
a_by_getattr = getattr(foo, "a")
a_by_inspect = [member[1] for member in foo_members
if member[0] == "a"][0]
print (id(a_by_getattr), a_by_getattr, type(a_by_getattr))
print (id(a_by_inspect), a_by_inspect, type(a_by_inspect))
# (38842160L, <bound method Foo.method of <__main__.Foo object at 0x00000000025EF390>>, <type 'instancemethod'>)
# (39673576L, <bound method Foo.method of <__main__.Foo object at 0x00000000025EF390>>, <type 'instancemethod'>)
# (34072832L, 100, <type 'int'>)
# (34072832L, 100, <type 'int'>)
Run Code Online (Sandbox Code Playgroud)
对于'a'属性,getattr和inspect.getmembers返回相同的对象.但是对于方法'方法',它们返回不同的对象(可以通过不同的id看到).
为什么会这样?
millimoose得到绿色检查,但我想我会添加一点.
TL;博士
绑定方法对象是瞬态的.也就是说,每次抓住它们时都会重新创建它们.
class Foo(object):
def bar(object): pass
foo = Foo()
m1 = foo.bar
m2 = foo.bar
print (id(m1))
print (id(m2))
# 38121264
# 38952752
Run Code Online (Sandbox Code Playgroud)
更多细节:描述符协议
每次抓取绑定方法对象时都会重新创建绑定方法对象,因为存储在类中的函数对象实现了描述符协议.
bar函数存储在类dict中:
class Foo(object):
def bar(object): pass
print (Foo.__dict__['bar'])
# <function bar at 0x00000000025F2208>
Run Code Online (Sandbox Code Playgroud)
当Foo实例尝试访问bar时,它没有它.所以它在它的类上查找并在类dict中找到它.根据描述符协议,函数对象实现__get__方法.那么实际上调用绑定方法的是:
print(Foo.__dict__['bar'].__get__(foo, Foo))
# <bound method Foo.bar of <__main__.Foo object at 0x00000000025719B0>>
Run Code Online (Sandbox Code Playgroud)
这相当于:
print (foo.bar)
# <bound method Foo.bar of <__main__.Foo object at 0x00000000025719B0>>
Run Code Online (Sandbox Code Playgroud)
您可以像这样获得原始函数:
print (foo.bar.im_func) # Python 2.x
# <function bar at 0x00000000025F2208>
print (foo.bar.__func__) # Python 3.x
# <function bar at 0x00000000025F2208>
Run Code Online (Sandbox Code Playgroud)
我稍微重组了你的例子,以更好地说明我将用来解释行为的变体
import inspect
def print_id(obj):
print "{} => {}".format(id(obj), obj)
def getmember(obj, name):
#members = dict(inspect.getmembers(obj))
#return members[name]
return [member
for _name, member in inspect.getmembers(obj)
if name == _name][0]
class Foo(object):
def bar(self): pass
foo = Foo()
m1 = foo.bar
m2 = getattr(foo, 'bar')
m3 = getmember(foo, 'bar')
print_id(m1)
print_id(m2)
print_id(m3)
Run Code Online (Sandbox Code Playgroud)
但是,如果您检查 REPL 中的对象,您的代码的基本结构可能如下所示:
#...
foo = Foo()
print_id(foo.bar)
print_id(getattr(foo, 'bar'))
print_id(getmember(foo, 'bar'))
Run Code Online (Sandbox Code Playgroud)
该id()函数基本上返回对象的内存地址。也就是说,它不是在程序的整个运行时创建的所有对象之间唯一的标识。它仅在任何给定时间点存在于流程中的所有对象之间是唯一的。
适合这两个示例之间差异的解释是,foo.bar以三种方式中的任何一种进行解析每次都会为您提供一个新对象。在第一个示例中,这些对象存储在临时变量中,因此所有三个对象都必须位于不同的内存地址。
在第二个例子中,绑定的方法对象打印出来后就不再需要了;Python 引用计数 GC 将释放其内存。这意味着下次创建绑定的方法对象时,它是一个新对象,碰巧在与前一个相同的内存地址处创建。这就是为什么看起来您多次获得同一个对象的原因。
你总是得到一个新的绑定方法对象可以简单地显示:
>>> foo.bar == foo.bar
True
>>> foo.bar is foo.bar
False
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6169 次 |
| 最近记录: |