高品質コードの作成--pythonプログラムの改善の提案(六)

7056 ワード

原文は私のブログのホームページに発表して、転載して出典を明記してください!
提案二十八:可変オブジェクトと非可変オブジェクトpythonのすべてのオブジェクトを区別し、各オブジェクトには一意の識別子(id())、タイプ(type()および値があり、オブジェクトはその値に基づいて可変オブジェクトと非可変オブジェクトに変更できるかどうか、その数値、文字列、メタグループが可変オブジェクトに属し、辞書およびリスト、バイト配列は可変オブジェクトに属します.プログラムを見てみましょう
class Student(object):
    def __init__(self,name,course=[]):
        self.name = name
        self.course = course

    def addcourse(self,coursename):
        self.course.append(coursename)

    def printcourse(self):
        for item in self.course:
            print item

xl = Student('xl')
xl.addcourse('computer')
xl.addcourse('automation')
print xl.name + "'s course:"
xl.printcourse()
print "~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
cyj = Student('cyj')
cyj.addcourse('software')
cyj.addcourse('NLP')
print cyj.name + "'s course:"
cyj.printcourse()

実行結果は初心者を驚かせます.
xl's course:
computer
automation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cyj's course:
computer
automation
software
NLP

xlとcyjのcourse変数のidを表示すると、メモリ内の同じアドレスを指す値が同じであることがわかりますが、xlとcyjは2つの異なるオブジェクトです.この2つのオブジェクトをインスタンス化する場合、2つのオブジェクトには異なるメモリ領域が割り当てられ、init()関数を呼び出して初期化されますが、init()関数の2番目のパラメータはデフォルトパラメータであるため、デフォルトパラメータは関数呼び出し時に1回しか評価されず、以降は最初の評価の結果が使用されます.したがって、実際にオブジェクト空間内のcourseはlistのアドレスを指しています.この場合、可変オブジェクトをデフォルトパラメータとして使用する場合は、可変オブジェクトの変更が元のオブジェクトに直接影響することを警戒します.次のように解決できます.
    def __init__(self,name,course=None):
        self.name = name
        if course is None:course = []
        self.course = course

可変オブジェクトの場合、pythonは実際には元の値を保存し、新しいオブジェクトを再作成します.2つのオブジェクトが同時に1つの文字列オブジェクトを指す場合、1つのオブジェクトに対する操作は他のオブジェクトに影響しません.例:
str1 = "write pythonic code"
str2 = str1
str1 = str1[-4:]
print id(str1)
print id(str2)
print str1
print str2

提案二十九:および{}:コンシステンシコンテナ初期化形式リストは、その柔軟性が実際の応用で広く使用されているため、有用なデータ構造である.リストにとって、リスト解析はよく使われます.リスト解析の構文は、iterableの各要素を反復し、条件が満たされると式exprで計算された内容に基づいて要素を生成し、新しいリストに入れ、順番に類推し、最終的にリスト全体に戻る.
[expr for iter_item in iterable if cond_expr]

リスト解析の使用は非常に柔軟です.
  • は、複数のネストをサポートし、2次元リストを生成する必要がある場合は、リストを使用してネストを解析する方法
    nested_list = [['Hello', 'World'],['Goodbye', 'World']]
    nested_list =  [[ele.upper() for ele in word] for word in nested_list]
  • を使用することができる.
  • は多重反復
    [(a,b) for a in ['1', '2', '3', '4'] for b in ['a', 'b', 'c', 'd'] if a != b]
  • をサポートする.
  • 式は、単純な式であってもよいし、複雑な式であってもよいし、関数
    def f(v):
    if v%2 == 0:
        v = v ** 2
    else:
        v = v + 1
    return v
    print [f(v) for v in [1,2,3,-1] if v > 0]
    print [v ** 2 if v %2 == 0 else v + 1 for v in [1,2,3,-1] if v > 0]
  • であってもよい.
  • iterableは、任意の反復可能オブジェクト
    fp = open('wdf.py','r')
    res = [i for i in fp if 'weixin' in i]
    print res
  • であることができる.
    なぜリストを生成する必要がある場合にリスト解析を推奨するのでしょうか.
  • リスト解析を使用すると、より直感的で明確になり、コードがより簡潔になります.
  • リスト解析の効率はより高いが、ビッグデータ処理ではリスト解析が最適ではなく、メモリ消費量が多すぎるとMemoryError
  • につながる可能性がある.
    リスト解析の構文を使用できるほか、以下のように組み込まれたデータ構造もサポートされています.
    #generator
    (expr for iter_item in iterable if cond_expr)
    #set
    {expr for iter_item in iterable if cond_expr}
    #dict
    {expr1: expr2 for iter_item in iterable if cond_expr}

    提案30:関数伝達パラメータは伝達値でも伝達参照でもないことを覚えておく.pythonにおける関数伝達パラメータについては、従来の3つの観点がある.
  • 伝値
  • 伝引用
  • 可変オブジェクト参照、非可変オブジェクト値
  • これらの理解はすべて少しずれていて、pythonの中の賦値は私たちが理解しているC/C++などの言語の賦値の意味とは違います.C/C++とpythonがどのように動作しているかを例にとると
    a = 5, b= a, b = 7

    C/C++でb=aが実行されると、メモリにメモリを申請してそのメモリにaの値をコピーし、b=7が実行された後にbの対応する値を5から7 pythonに変更することはコピーではなく、b=a操作でbがaと同じオブジェクトを参照し、b=7がbをオブジェクト7に指向するので、python関数パラメータに対しては値伝達でも参照伝達でもなく、伝達対象または伝達対象の参照であるべきである.関数パラメータは、伝達中にオブジェクト全体を伝達します.可変オブジェクトの変更は、関数の外部および内部で表示され、呼び出し元と呼び出し元の間でこのオブジェクトが共有されますが、可変オブジェクトでは、本当に変更されないため、変更は新しいオブジェクトを生成して値を割り当てることによって実現されます.
    提案三十一:変長パラメータpythonは可変長のパラメータリストをサポートすることを慎み、関数定義時に*argsと*kwargsの2つの特殊な文法を使用することで実現することができる.argsを使用して可変パラメータリストを実装します.argsは、キーワード以外のパラメータを渡すためにメタグループ形式にパッケージされたパラメータリストを受信するために使用されます.パラメータの個数は任意です.
    def sumf(*args):
        res = 0
        for x in args[:]:
            res += x
        return res
    
    print sumf(2,3,4)
    print sumf(1,2,3,4,5)

    *kwargsを使用して、辞書形式のキーワードパラメータのリストを受信します.辞書のキー値は、可変パラメータのパラメータ名と値を表します.
    def category_table(**kwargs):
        for name, value in kwargs.items():
            print '{0} is a kind of {1}'.format(name, value)
    
    category_table(apple = 'fruit', carrot = 'vegetable')

    通常パラメータ、デフォルトパラメータ、および上記の2つのパラメータが同時に存在する場合、通常パラメータとデフォルトパラメータに優先的に値が割り当てられますが、なぜ可変長パラメータを慎むのでしょうか.
  • の使用が柔軟すぎて、コードがはっきりしていない
  • です.
  • 関数のパラメータリストが長い場合、*argsおよび*kwargsを使用することによって関数の定義を簡略化することができるが、通常、この関数はより良い実装方法があり、再構築されるべきであることを意味する.
  • 可変長パラメータは、
  • の場合に適しています.
  • 関数にデコレーション
  • を追加
  • パラメータの数が不確定である場合、変長パラメータ
  • を用いることが考えられる.
  • 関数のマルチステートを実装するために使用されるか、または継承された場合、サブクラスが親クラスのいくつかのメソッドを呼び出す必要がある場合、
  • .
    パラメータ32:str()とrepr()の違いを深く理解する2つの方法はpythonのオブジェクトを文字列に変換することができます.彼らの使用と出力は非常に似ています.違いは?
  • 両者の間の目標は異なる:str()は主にユーザ向けであり、その目的は可読性であり、戻り形式はユーザ友好性と可読性の両方が強い文字列タイプであり、repr()はpython解釈器向けであり、あるいは開発者向けであり、その目的は正確性であり、その戻り値はpython解釈器の内部関数を表し、debug
  • としてよく用いられる.
  • は、解釈器にaを直接入力ときにrepr()関数をデフォルトで呼び出し、print aはstr()関数
  • を呼び出す.
  • repr()の戻り値は、一般にeval()関数で
    obj == eval(repr(obj))
  • を復元することができる.
  • この2つのメソッドは、それぞれ組み込まれた__を呼び出します.str__および_repr__()メソッドは、一般的にクラスで後者を定義すべきであるが、前者のメソッドはオプションであり、ない場合、デフォルトでは後者の結果を使用してオブジェクトの文字列表現形式
  • を返す.
    提案33:staticmethodとclassmethodを区別する使用シーンpythonの静的メソッド(staticmethod)とクラスメソッド(classmethod)は、次のように装飾器に依存して実現されます.
    #staticmethod
    class C(object):
        @staticmethod
        def f(arg1, arg2, ...):
    #classmethod
    class C(object):
        @classmethod
        def f(arg1, arg2, ...):

    静的メソッドもクラスメソッドもクラス名で使用できる.メソッド名またはインスタンス.メソッド名の形式でアクセスします.ここで、静的メソッドには、バインド、非バインド、暗黙的パラメータなどの通常のメソッドの特殊な動作はありません.一方、クラスメソッドの呼び出しは、クラス自体を暗黙的なパラメータとして使用しますが、呼び出し自体は、パラメータの提供を表示する必要はありません.では、なぜ静的メソッドとクラスメソッドが必要なのでしょうか.フルーツ類Fruitがあると仮定し、属性totalで総量を表し、set()で重量を設定し、print_total()メソッドで果物の数を印刷します.類Appleと類OrangeはFruitから継承され、現在、異なるタイプの果物の総量をそれぞれ追跡し、実現方法をまとめる必要がある.
  • は、AppleクラスとOrangeクラスでクラス変数totalをそれぞれ定義し、ベースクラスのset()とprint_を上書きする一般的なインスタンスメソッドを使用して実現される.total()メソッド
  • クラスメソッドを用いて
    class Fruit(object):
    total = 0
    def print_total(cls):
        print cls.total
    @classmethod
    def set(cls, value):
        cls.total = value
    class Apple(Fruit):
    pass
    class Orange(Fruit):
    pass
    app1 = Apple()
    app1.set(200)
    app2 = Apple()
    org1 = Orange()
    org1.set(300)
    org2 = Orange()
    app1.print_total()
    org1.print_total()
    の簡単な解析を実現すると,異なる種類の果物オブジェクトに対してset()メソッドを呼び出す際にステルスに伝達されるパラメータがそのオブジェクトに対応するクラスであり,set()を呼び出す過程で対応するクラスのクラス変数が動的に生成されることが分かる.静的メソッドは、通常、特定のインスタンスにも特定のクラスにも関連しないメソッドに適用されます.彼はクラスに存在し、外部関数よりもコードを効率的に組織することができ、関連コードの垂直距離をより近くし、コードのメンテナンス性を高めることができます.

  • 参考:高品質コードの作成--pythonプログラムを改善する91の提案