68

大きな Python 関数を小さな関数にリファクタリングしたいと考えています。たとえば、次のコード スニペットを考えてみましょう。

x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9

もちろん、これは些細な例です。実際には、コードはより複雑です。私のポイントは、抽出された関数に渡す必要がある多くのローカルスコープ変数が含まれているということです。これは次のようになります。

def mysum(x1, x2, x3, x4, x5, x6, x7, x8, x9):
    x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
    return x

問題は、Pylint が引数が多すぎるという警告をトリガーすることです。

次のようなことを行うことで、警告を回避できます。

def mysum(d):
    x1 = d['x1']
    x2 = d['x2']
    ...
    x9 = d['x9']
    x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9
    return x

def mybigfunction():
    ...
    d = {}
    d['x1'] = x1
    ...
    d['x9'] = x9
    x = mysum(d)

しかし、このアプローチは私には醜いです。冗長でさえある多くのコードを書く必要があります。

それを行うより良い方法はありますか?

4

10 に答える 10

141

まず、ペルリスのエピグラムの1 つ:

「10個のパラメーターを持つプロシージャーがある場合、おそらくいくつかを見逃しています。」

10 の引数のいくつかは、おそらく関連しています。それらをオブジェクトにグループ化し、代わりに渡します。

質問には直接答えるのに十分な情報がないため、例を作成します。

class PersonInfo(object):
  def __init__(self, name, age, iq):
    self.name = name
    self.age = age
    self.iq = iq

次に、10 引数の関数:

def f(x1, x2, name, x3, iq, x4, age, x5, x6, x7):
  ...

になります:

def f(personinfo, x1, x2, x3, x4, x5, x6, x7):
  ...

呼び出し元は次のように変更されます。

personinfo = PersonInfo(name, age, iq)
result = f(personinfo, x1, x2, x3, x4, x5, x6, x7)
于 2009-05-03T07:22:58.563 に答える
68

引数を渡すためのより良い方法が必要ですか、それとも単に Pylint が苦労するのを止める方法が必要ですか? 後者の場合、次の行に沿って Pylint 制御コメントをコードに入れることで、しつこいことを止めることができます。

#pylint: disable=R0913

または、より良い:

#pylint: disable=too-many-arguments

できるだけ早くオンに戻すことを忘れないでください。

私の意見では、多くの引数を渡すことに本質的に問題はなく、それらをすべてコンテナ引数にまとめることを提唱する解決策は、Pylintがあなたをしつこくするのを止めることを除いて、実際には何の問題も解決しません:-)。

20 個の引数を渡す必要がある場合は、それらを渡します。関数がやりすぎてリファクタリングが役立つ可能性があるため、これが必要になる可能性があります。しかし、「実際の」コードが何であるかを確認しない限り、実際に下すことができる決定ではありません。

于 2009-05-03T07:59:40.147 に答える
39

Pylint では、引数の最大許容数を簡単に変更できます。pylintrcファイルを開き(まだ持っていない場合は生成します)、次のように変更します。

max-args = 5

に:

max-args = 6 # Or any value that suits you

Pylintのマニュアルから

セットアップとコーディング標準に適したすべてのオプションを指定するのは面倒なので、rc ファイルを使用してデフォルト値を指定することができます。Pylint は /etc/pylintrc と ~/.pylintrc を探します。--generate-rcfile オプションは、標準出力の現在の構成に従ってコメント付きの構成ファイルを生成し、終了します。このオプションの前に他のオプションを配置して構成で使用するか、デフォルト値から開始して手動で構成を調整することができます。

于 2009-05-03T10:58:03.400 に答える
11

Python の可変引数機能を使用してみることができます。

def myfunction(*args):
    for x in args:
        # Do stuff with specific argument here
于 2009-05-03T05:17:42.470 に答える
8

おそらく、いくつかの引数をメンバー変数に変えることができます。それだけの状態が必要な場合、クラスは私にとって良い考えのように思えます。

于 2009-05-03T05:18:05.480 に答える
6

関数を単純化または分割して、9 つの引数を必要としないようにします (または Pylint を無視しますが、提案しているような回避策は、lint ツールの目的を無効にします)。

一時的な措置である場合は、Pylint: Disable-msg for a block or statement? で説明されているように、コメントを使用して問題の特定の関数の警告を無効にします。

後で、無効になっているすべての警告を grep できます。

于 2009-05-03T05:16:17.853 に答える
6

番号を参照するのは好きではありません。シンボリック名はより表現力があり、時間の経過とともに陳腐化する可能性のあるコメントを追加する必要がなくなります。

だから私はむしろやりたい:

#pylint: disable-msg=too-many-arguments

また、そこにぶら下がったままにしないことをお勧めします。ファイルが終了するか無効になるまで、アクティブなままになります。

だからもっと良い:

#pylint: disable-msg=too-many-arguments
code_which_would_trigger_the_msg
#pylint: enable-msg=too-many-arguments

行ごとに 1 つの警告/エラーを有効/無効にすることもお勧めします。

于 2014-11-06T11:31:02.083 に答える
0

Python には、ユーザーのニーズにうまく適合する可能性が高い優れた関数型プログラミング ツールがいくつかあります。ラムダ関数mapを確認してください。また、リストの方がはるかに優れていると思われる場合は、辞書を使用しています。あなたが提供した簡単な例については、このイディオムを試してください。map の方が優れていて高速ですが、ニーズに合わない場合があることに注意してください。

def mysum(d):
   s = 0  
   for x in d:
        s += x
   return s

def mybigfunction():
   d = (x1, x2, x3, x4, x5, x6, x7, x8, x9)
   return mysum(d)

多くのローカル変数があると言いましたが、率直に言って、リスト (またはタプル) を扱っている場合は、長期的にはリストを使用し、それらすべてのローカル変数を除外する必要があります。

于 2009-05-03T05:19:36.130 に答える