6

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'>)

'a'属性の場合、getattrとinspect.getmembersは同じオブジェクトを返します。しかし、メソッド'method'の場合、それらは異なるオブジェクトを返します(異なるIDで確認できます)。

なんでそうなの?

4

2 に答える 2

10

ミリムースは緑色のチェックを取得しますが、少し追加すると思いました。

tl;dr

バインドされたメソッド オブジェクトは一時的です。つまり、それらを取得するたびに新しく作成されます。

class Foo(object):
    def bar(object): pass

foo = Foo()
m1 = foo.bar
m2 = foo.bar
print (id(m1))
print (id(m2))

# 38121264
# 38952752

詳細: 記述子プロトコル

クラスに格納されている関数オブジェクトは記述子プロトコルを実装しているため、バインドされたメソッド オブジェクトは取得するたびに新しく作成されます。

bar 関数は、クラス dict に格納されます。

class Foo(object):
    def bar(object): pass

print (Foo.__dict__['bar'])

# <function bar at 0x00000000025F2208>

Foo インスタンスが bar にアクセスしようとすると、bar がありません。そのため、そのクラスを調べて、クラス辞書で見つけます。関数オブジェクトは、記述子プロトコルに従って__get__メソッドを実装します。したがって、バインドされたメソッドを取得するために実際に呼び出されるのは次のとおりです。

print(Foo.__dict__['bar'].__get__(foo, Foo))

# <bound method Foo.bar of <__main__.Foo object at 0x00000000025719B0>>

これは次と同等です:

print (foo.bar)

# <bound method Foo.bar of <__main__.Foo object at 0x00000000025719B0>>

次のように元の関数を取得できます。

print (foo.bar.im_func) # Python 2.x

# <function bar at 0x00000000025F2208>    

print (foo.bar.__func__) # Python 3.x

# <function bar at 0x00000000025F2208>   
于 2012-10-12T12:04:58.000 に答える
6

動作を説明するために使用するバリアントをわかりやすく説明するために、例を少し再構成しました

一時変数を使用

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)

ただし、REPLでオブジェクトを検査する場合、コードの基本構造はおそらく次のようになります。

一時変数なし

#...
foo = Foo()

print_id(foo.bar)
print_id(getattr(foo, 'bar'))
print_id(getmember(foo, 'bar'))

このid()関数は基本的にオブジェクトのメモリアドレスを返します。つまり、プログラムの実行時間全体で作成されたすべてのオブジェクト間で一意のIDではありません。これは、任意の時点でプロセスに存在するすべてのオブジェクト間でのみ一意です。

2つの例の違いに当てはまる説明foo.barは、3つの方法のいずれかで解決すると、毎回新しいオブジェクトが得られるということです。最初の例では、これらのオブジェクトは一時変数に格納されているため、3つすべてを異なるメモリアドレスに配置する必要があります。

2番目の例では、バインドされたメソッドオブジェクトは、印刷後に不要になります。Python参照カウントGCは、そのメモリを解放します。これは、次にバインドされたメソッドオブジェクトが作成されるときに、前のオブジェクトと同じメモリアドレスで作成される新しいオブジェクトであることを意味します。これが、同じオブジェクトを複数回取得しているように見える理由です。

常に新しいバインドされたメソッドオブジェクトを取得することは、簡単に示すことができます。

>>> foo.bar == foo.bar
True
>>> foo.bar is foo.bar
False
于 2012-10-11T22:03:42.223 に答える