🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

829閲覧

f.__name__ = unbound_method.__name__のような記述は何を意味しているのでしょうか?

ao_tombo

総合スコア7

Python 2.7

Python 2.7は2.xシリーズでは最後のメジャーバージョンです。Python3.1にある機能の多くが含まれています。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2021/03/17 05:19

オライリーのPythonクックブック第二版の中で以下のようなPython2.3や2.4で書かれた古いコードがあります。

Python2

1class Proxy(object): 2 """ すべてのプロキシクラスのベースクラス """ 3 def __init__(self, obj): 4 super(Proxy, self).__init__() 5 self._obj = obj 6 def __getattr__(self, attrib): 7 return getattr(self._obj, attrib) 8def make_binder(unbound_method): 9 def f(self, *a, **k): 10 return unbound_method(self._obj, *a, **k) 11 # Python2.4以降ではこうする 12 f.__name__ = unbound_method.__name__ 13 return f 14known_proxy_classes = {} 15def proxy(obj, *specials): 16 """ 特殊メソッドを移譲できるプロキシを作るファクトリ関数 """ 17 # 適当なカスタムクラスがあるのでは? 18 obj_cls = obj.__class__ 19 key = obj_cls, specials 20 cls = known_proxy_classes.get(key) 21 if cls is None: 22 # 既存のプロキシクラスがないので自動生成する 23 cls = type("%sProxy" % obj_cls.__name__, (Proxy,), {}) 24 for name in specials: 25 name = '__%s__' % name 26 unbound_method = getattr(obj_cls, name) 27 setattr(cls, name, make_binder(unbound_method)) 28 # 次に呼び出された時のためにキャッシュする 29 known_proxy_classes[key] = cls 30 # 必要なプロキシをインスタンス化して返す 31 return cls(obj) 32

Python2

1def empty_copy(obj): 2 class Empty(obj.__class__): 3 def __init__(self): pass 4 newcopy = Empty() 5 newcopy.__class__ = obj.__class__ 6 return newcopy 7 8class YourClass(object): 9 def __init__(self): 10 ## assume there's a lot of work here 11 def __copy__(self): 12 newcopy = empty_copy(self) 13 ## copy some relevant subset of self's attributes to newcopy 14 return newcopy 15 16if __name__ == '__main__': 17 import copy 18 y = YourClass() # This, of course, does run __init__ 19 print(y) 20 z = copy.copy(y) # ...but this doesn't 21 print(z) 22

これらの中で、

Python2

1# Python2.4以降ではこうする 2f.__name__ = unbound_method.__name__

Python2

1newcopy.__class__ = obj.__class__

この部分が何のためにこのような処理を書き加えているのか教えて頂けないでしょうか?

また、現在のPython3でも上記コードは動きますが、Python3.x現在でも上記のようなコードを書き加えるものなのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuki23

2021/03/17 05:49

その本には何と書いてありますか?
ao_tombo

2021/03/17 06:19

質問ありがとうございます。以下のように書かれています。 上のコードの説明が、 「プロキシやメソッドの自動委譲は、Pythonでは__getattr__メソッドがあるおかげで楽に書くことが出来る。Pythonは属性の参照を行って上手くいかなかった場合、__getattr__メソッドを自動的に呼び出す。従来のオブジェクトモデルでは、__getattr__は特殊メソッドに対しても通常通りに適用された。(2007年)現在、新規に書くコードについては新しいスタイルのオブジェクトのみを使うことが推奨されている。いつの日か、これから何年か後にはPython3.0が従来のオブジェクトモデルを排除することになっている。新しいスタイルのオブジェクトモデルでは、Pythonは実行時に特殊メソッドの検索を行わず、クラスオブジェクトに用意された『スロット』のみを参照する。スロットはクラスオブジェクトが作成または修正された時のみ更新される。このため、ラッピングされたオブジェクトに特殊メソッドを委譲したいプロキシオブジェクトは、それ専用に仕立て上げられたクラスに属している必要がある。幸いにもレシピで示した通り、クラスを実行時に生成してインスタンス化するのはPythonではとても簡単である。~~~」 下のコードの説明が、 「__init__メソッドがハイコストなため、__copy__メソッドで当該のクラス(つまりself)の空インスタンスをまず作ることでこれをバイパスしたい場合も多い。もっともシンプルで汎用的な方法は、Pythonがインスタンスのクラスを動的に変更できることを利用するものである。つまりこのレシピのように、空のインスタンスを作るためのクラスをローカルに用意してそのクラスで新しいオブジェクトを作り、その__class__属性を差し替えてしまう方法である。空のインスタンスを作るEmptyクラスが、コピーしようとするオブジェクトのクラスobj.__class__から継承する部分は無害ではあるものの古いスタイルのクラスにおいては冗長な書き方かもしれない。しかしこの継承によって、このレシピは新旧両方ののクラススタイルで動作することが可能となっている。obj.__class__の継承を行う場合、Emptyクラスの__init__メソッドはオーバーライドする必要があり、そうしなければこのレシピは意味が無くなってしまう。こうして目的のクラスの空オブジェクトを作ったら、たいていはコピー元からselfへと、属性の一部をコピーする必要がある。~~~」 、となっています。分かったような分からないような...という感じです。
t_obara

2021/03/17 07:09

desriptorあたりを調べれば理解が進みそうですね
ao_tombo

2021/03/18 08:02

アドバイスありがとうございます。デスクリプタも学習してみます。
guest

回答2

0

ベストアンサー

f.name = unbound_method.name について

A. 関数内で一時的にクラスや関数を動的に生成してるので、必要な情報のコピーしてます。

python

1def make_binder(unbound_method): 2 # unbound_method の実態は、各メソッドの関数で 3 # 引数に インスタンス(self._obj) を加えて呼び出す新たな関数を 4 # この関数内部で作ってます。 5 6 def f(self, *a, **k): 7 return unbound_method(self._obj, *a, **k) 8 9 # この時点で f.__name__ は関数名の "f" 10 11 # make_binder で内部生成された関数は全て "f" という名前になり 12 # 区別がつかなくなってしまうので、元の関数名をコピーします。 13 14 f.__name__ = unbound_method.__name__ 15 16 17 return f

スタックトレース情報のログに現れたり、
対話環境で、メソッドの情報を調べたりする時に用いたりします。

__name__ については、関数名・メソッド名の表示情報だけなので
そのレシピの本題である「委譲」自体の実行には支障ありません。

python

1def funcA(): 2 "function A" 3 pass 4 5# 関数の情報を得られる 6print(funcA.__name__) # => "funcA" 7print(funcA.__doc__) # => "function A" 8 9def bind_nothing(func): 10 def f(*a, **kw): 11 return func(*a, **kw) 12 return f 13 14# 内部で生成された f() の情報が得られる 15funcB = bind_nothing(funcA) 16print(funcB.__name__) # => "f" 17print(funcB.__doc__) # => None 18 19 20def bind_with_name(func): 21 def f(*a, **kw): 22 return func(*a, **kw) 23 f.__name__ = func.__name__ # 元の関数の情報をラップした関数へコピー 24 return f 25 26funcC = bind_with_name(funcA) 27print(funcC.__name__) # => "funcA" <-- 元の関数名・メソッド名が得られる

このような内部関数で、関数名以外の情報をコピーするには、標準ライブラリの functools.wrapsを使って下さい。

他の __xxx__ の情報については、inspect 活動中のオブジェクトの情報を取得する モジュール辺りを参照。
内部情報的なもので、主にデバッガ等で用いられます。


現在のPython3でも上記コードは動きますが、Python3.x現在でも上記のようなコードを書き加えるものなのでしょうか?

__name____class__ については、2.4以降 なので、共通です。

他の部分、クラスの定義の違いは、3.x 現在では区別不要ですが、2.x -> 3.x への移行期は
2.x と互換性のあるコードというのを明示する為に object 継承を記述したりしました。

python2

1 2# python 2.7 3 4class YourClass: # old-style class 5 6class YourClass(object): # new-style class 7

python3

1 2# python 3.x 3 4class YourClass: # new-style class 5 6class YourClass(object): # new-style class 7

投稿2021/03/17 07:44

teamikl

総合スコア8722

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ao_tombo

2021/03/18 08:05

ありがとうございます!説明のコードが大変分かりやすかく、ようやく理解出来ました。ありがとうございました。
guest

0

下の方は以下のコードの実行結果を考えてください。

>>> class A: ... def a(self): ... print('Aです') >>> class B: pass >>> o = B() >>> o.__class__ = A >>> o.a() Aです

o.aが参照された時、属性aに関して、まずインスタンス変数を探索します。なかったなら次はクラスから探索します。
__class__を差し替えることでそこに干渉して、探索先をBからAにしています。
クラスAaという名前を関数オブジェクトが束縛しているので、属性a探し出されたことになって、結果メソッドとして実行されています。

newcopy.__class__ = obj.__class__によって、インスタンス変数から見つからなかった属性の探索先がEmptyからobj.__class__に差し替わるので、クラス変数やメソッドへの参照がobj.__class__に対しておこなわれるようになります。


上の方は、これを無くしたとしても実行時に問題は起きることはないんじゃないでしょうか。(やっておくに越したことはないにせよ)
無いときの弊害はちょっと思いつきません。

投稿2021/03/17 07:22

quickquip

総合スコア11231

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ao_tombo

2021/03/18 08:08 編集

なるほど、そのような挙動が行われていたのですね。大変参考になりました。教えて頂きありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問