質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.35%
Python

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

Q&A

解決済

2回答

1573閲覧

Python でメソッドの後付け的なことをするにはどうすればよいのか?

Paalon

総合スコア266

Python

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

0グッド

1クリップ

投稿2021/12/11 08:22

編集2021/12/11 09:10

普段 Julia を書いているのですが、Python でどう管理すればよいのか分からないことがあります。
クラスによって異なる実装になるかもしれないのですが、同じ意味の値を返す関数をいろんな既存のモジュールのクラスについて定義したいのですが、やり方がよく分かりません。自作のモジュールなら、そのモジュールをいじって、該当クラスのメソッドを追加すれば良いだけですが、既存のモジュールにその関数を別名を付けないで実装するにはどうすれば良いのでしょうか?Python では、同じ名前を付けたい場合は、関数の引数の型に関するオーバーロードはないので、多くの場合、クラスのメソッドとしてその機能を提供すると思うのですが、既存のクラスをいじりたい場合はどうすれば良いのでしょうか?

現在はとりあえず、型ごとに別名を関数のお尻に付けて対処していますが、段々増えてきて煩雑になってきてしまったのでどうにかしたいです。

例えば、現在、

python

1import modulea 2import moduleb 3 4def func1_a(a1: modulea.TypeA, a2: modulea.TypeA) -> modulea.TypeA: 5 a = a1 + a2 6 return a 7def func1_b(b1: moduleb.TypeB, b2: moduleb.TypeB) -> moduleb.TypeB: 8 b = b1 + b2 9 return b 10def func2_a(a1: modulea.TypeA, a2: modulea.TypeA) -> modulea.TypeA: 11 a = a1 - a2 12 return a 13def func2_b(b: moduleb.TypeB, b2: moduleb.TypeB) -> moduleb.TypeB: 14 b = b1 - b2 15 return b 16 17# 関数を使うとき 18 19func1_a(a1, a2) 20func1_b(b1, b2) 21func2_a(a1, a2) 22func2_b(b1, b2)

こんな感じになっているのを

python

1func1(a1, a2) 2func1(b1, b2) 3func2(a1, a2) 4func2(b1, b2)

または

python

1a1.func1(a2) 2b1.func1(b2) 3a1.func2(a2) 4b1.func2(b2)

として管理したいということです。

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

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

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

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

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

BeatStar

2021/12/11 08:50

意味がよくわかりません。どういうことでしょうか? ざっくりとで良いので、サンプルを書いてくれませんか? そうしないと質問者さんの意図と違う解釈で回答が付く可能性が高いです。
ozwk

2021/12/11 09:29

ジェネリクス的なことですか?
Paalon

2021/12/11 09:54

Java のジェネリクスにはあまり詳しくないのですが、そういうような、多態性を実現して管理したいということです。
quickquip

2021/12/11 22:26

func1_aとfunc1_bが分かれている方が管理/保守しやすいという考えは理解しますが、最後に欲しいのがfunc1なら、最初からfunc1だけを書いて管理/保守した方がまだマシなのでは? としか思わないんですか……。 「何が問題なのか」「何を解決したいのか」がぼんやりしています。多分提示されたコードに意味がなくて、意味論の方から考えられないせいだろうと思いますが。
Paalon

2021/12/12 13:05

最初から func1 だけを書いて実装する方法というのが分かりません。ここで func1_a と func1_b というのは一般的に異なる型を受け取り、異なる処理をする関数です。
guest

回答2

0

ベストアンサー

事前にこういうことをやっておけば、

python

1modulea.TypeA.func1 = func1_a 2moduleb.TypeB.func1 = func1_b 3modulea.TypeA.func2 = func2_a 4moduleb.TypeB.func2 = func2_b

このように書くことは可能。

python

1a1.func1(a2) 2b1.func1(b2) 3a1.func2(a2) 4b1.func2(b2)

ただし、該当のクラスが特殊な型(組み込み型やC拡張モジュールで定義された型など)だとできない。
(こちらの記事の方法であれば可能: [Python] build-in objectに独自のメソッドを追加する禁断の果実)
あと、事前に別名で関数として定義しなければならないので、名前付けの問題は残る。
(直接ラムダ式を代入することもできるけど、ラムダ式だとできることが限られる)

他に、functools.singledispatchを使う方法もある。

python

1from functools import singledispatch 2 3@singledispatch 4def func1(arg1, arg2): 5 pass 6@func1.register 7def _(a1: modulea.TypeA, a2: modulea.TypeA) -> modulea.TypeA: 8 a = a1 + a2 9 return a 10@func1.register 11def _(b1: moduleb.TypeB, b2: moduleb.TypeB) -> moduleb.TypeB: 12 b = b1 + b2 13 return b 14 15@singledispatch 16def func2(arg1, arg2): 17 pass 18@func2.register 19def _(a1: modulea.TypeA, a2: modulea.TypeA) -> modulea.TypeA: 20 a = a1 - a2 21 return a 22@func2.register 23def _(b: moduleb.TypeB, b2: moduleb.TypeB) -> moduleb.TypeB: 24 b = b1 - b2 25 return b

とすれば、このように書ける。(先頭引数の型しか見ていないので注意)

python

1func1(a1, a2) 2func1(b1, b2) 3func2(a1, a2) 4func2(b1, b2)

先頭引数以外の型も見たいのであれば、multipledispatchをインストールすれば可能。実行例は上と同じ。

python

1from multipledispatch import dispatch 2 3@dispatch() 4def func1(a1: modulea.TypeA, a2: modulea.TypeA) -> modulea.TypeA: 5 a = a1 + a2 6 return a 7@dispatch() 8def func1(b1: moduleb.TypeB, b2: moduleb.TypeB) -> moduleb.TypeB: 9 b = b1 + b2 10 return b 11@dispatch() 12def func2(a1: modulea.TypeA, a2: modulea.TypeA) -> modulea.TypeA: 13 a = a1 - a2 14 return a 15@dispatch() 16def func2(b: moduleb.TypeB, b2: moduleb.TypeB) -> moduleb.TypeB: 17 b = b1 - b2 18 return b

投稿2021/12/11 09:23

編集2021/12/11 22:09
actorbug

総合スコア2433

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

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

Paalon

2021/12/11 09:58 編集

具体的には直近で整理したいものは `numpy.ndarray` なので、 ```python import numpy def _f_numpy_ndarray(a): return a numpy.ndarray.f = _f_numpy_ndarray ``` と実装しようとすると `TypeError: can't set attributes of built-in/extension type 'numpy.ndarray'` と表示されるのですが、これは `numpy.ndarray` が組み込み型ということですか?
actorbug

2021/12/11 10:00

組み込み型以外でも無理な場合はあるので、できなければ別の方法を使いましょう。
Paalon

2021/12/11 10:21

functools.singledispatch を使う方法というのは標準的なのでしょうか?
actorbug

2021/12/11 10:25

標準的というのは、何を聞きたいのでしょうか。 リンク先を見れば分かりますが、functools.singledispatch は標準ライブラリの一部なので、使えない環境というのは基本的にはないと思われます。 この機能が一般的に使われる機会が多いかどうかは分かりません。
actorbug

2021/12/11 21:05

C拡張モジュールで定義された型だと、メソッドを追加できないようです。 以下手順で作成した型にメソッドを追加しようとすると、「TypeError: can't set attributes of built-in/extension type 'custom.Custom'」とエラーが出ます。 2. 拡張の型の定義: チュートリアル https://docs.python.org/ja/3/extending/newtypes_tutorial.html
guest

0

質問文の例だとこれでいい気がするのですがどうでしょう

python

1import numpy as np 2 3 4def add(a,b): 5 return a+b 6 7A = np.ones(3) 8B = 2 * np.ones(3) 9 10print(add(A,B)) # [3. 3. 3.] 11 12 13a = 1 14b = 2 15 16print(add(a,b)) # 3

投稿2021/12/12 13:23

編集2021/12/12 13:28
ozwk

総合スコア13553

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

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

Paalon

2021/12/12 13:30

この実装は numpy.ndarray と int という異なる型に対する、同じ処理をする実装で、異なる処理には対応していません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問