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

6578 ワード

原文は私のブログのホームページに発表して、転載して出典を明記してください!提案13:eval()のセキュリティ・ホールを警戒し、テキスト・データをよく処理している学生がeval()をやめられないと信じています.彼の使用は非常に簡単です.
eval("1+1==2")                                 #    
eval("'A'+'B'")                                #    
eval("1+2")                                    #    

pythonのeval()関数は、文字列strを有効な式として評価し、計算結果を返します.その関数は次のように宣言されます.
eval(expression[, globals[,locals]])
#globals     ,        
#locals       ,        

「eval is evil」という言葉は、eval()の安全性を主に狙ったevalに対する評価としてよく知られています.Webページがevalを呼び出してユーザーの入力に基づいてpython式の値を計算すると仮定します.ネットワーク環境で実行するユーザーが信頼できるわけではないため、eval()は任意の文字列を式として評価することができます.これは、ドリル可能な空きがあることを意味します.ユーザーが次のコードを入力すると、現在のディレクトリのすべてのファイルリストが得られます.
__import__("os").system("dir")

誰かが破壊しようとしたら、次の文字列(試行禁止)を入力すると、現在のディレクトリの下のすべてのファイルが削除されます.
__import__("os").system("del * /Q")

もちろんglobalsパラメータでは、演算範囲をいくつかの一般的な数学関数に限定するなど、グローバルネーミング空間へのアクセスを禁止できると言われています.
math_fun_list = ['acos', 'asin', 'atan', 'pi']
math_fun_dict = dict([(k, globals().get(k) for k in math_fun_list)])
eval(string, {"__builtins__":None}, math_fun_dict)

セキュリティの問題は解決しましたが、次の文字を入力してみてください.
[c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ == 'Quitter'][0](0)()
# ().__class__.__bases__[0].__subclasses__()  object      , Quitter “quite”    ,                。

したがって、実際の応用過程において、使用対象が信頼源でない場合、evalの使用をできるだけ避けるべきであり、evalを使用する必要がある場所で安全性の高いastを使用することができる.literal_eval代替.
提案14:enumerate()を使用してシーケンス反復のインデックスと値関数enumerate()を取得するのは、主にループ内でインデックスと対応する値を取得する問題を解決するためであり、必要に応じて1つの(index,item)ペアが生成されるたびに一定の不活性性を有する.次のように使用します.
enumerate(sequence, start=0)
#sequence   list、 set       
#   0  
#         ,    next()         

提案15:==isの使用シーンisはオブジェクト識別子(object identity)を表している.すなわち、2つのオブジェクトがメモリに同じメモリ空間を持っているかどうかを比較し、==は等しい(equal)を意味し、2つのオブジェクトの値が等しいかどうかを検証する.また、pythonにはstring interning(文字列が存在する)メカニズムがあります.小さい文字列の場合、システムのパフォーマンスを向上させるために値のコピーが保持され、新しい文字列を作成するときにそのコピーを直接指すようにします.長い文字列は存在しません.
推奨16:互換性を考慮して、できるだけUnicode pythonで構築された文字列を使用するには、strとUnicodeの2つのタイプがあります.strとUnicodeは、共通の祖先basestringを持っています.Unicodeは万国コードとも呼ばれ、各言語に一意のバイナリ符号化表現を設定し、デジタルコードから異なる言語文字セットへのマッピングを提供し、プラットフォーム間、誇張言語間のテキスト処理要求を満たすことができる.Unicode符号化システムは符号化方式と実現方式の2つの面に分けられ、符号化方式ではUCS-2とUCS-4の2つの方式に分けられ、UCS-2は2バイトで符号化され、UCS-4は4バイトで符号化される.1文字のUnicode符号化は決定されるが,実際の伝送過程では,システムプラットフォームの違いや省スペースの目的で実現方式が異なる.Unicodeの実現方式はUnicode変換フォーマットと称する、UTFと略称され、UTF-7、UTF-16、UTF-32、UTF-8などを含む、比較的一般的なものはUTF-8である.彼の特徴は、異なる範囲の文字に対して異なる長さの符号化を用いることであり、そのうち0 x 00~0 x 7 Fの文字のUTF-8符号化はASCII符号化と全く同じであり、UTF-8符号化の最大長は4バイトである.通常pythonで中国語の文字を処理すると、いくつかの問題が発生します.
  • 読み出しファイルの内容が文字化けして表示する
    fp = open("test.txt","r")
    print fp.read()
    fp.close()
    問題:読み込んだファイルtest.txtはUTF-8符号化形式で保存されているが、Windowsのローカルデフォルト符号化はCP 936であり、WindowsシステムではGBK符号化にマッピングされているため、UTF-8文字を直接表示する場合は互換性がない.解決策:まず読み込んだ文字をUTF-8で復号し,その後GBKで符号化する.
    fp = open("test.txt","r")
    print (fp.read().decode("utf-8")).encode("gbk")
    decode()メソッドは、他の符号化に対応する文字列をUnicodeに復号し、encode()メソッドはUnicode符号化を別の符号化に変換する.また、上記の例では(test.txtがNotepadソフトウェアを使用してUTF-8符号化で保存する場合など)異常が発生する可能性があります.これは、UTF-8符号化を保存する際に、ファイルの最初に見えない文字BOMを挿入し、codecsモジュールを使用することで、この問題を容易に処理できるソフトウェアがあるためです.
  • pythonソースファイルに中国語文字が含まれている場合syntaxError異常pythonのデフォルトの符号化はASCII符号化であり、受信機に文字の正確な処理方法を知らせるためにソースファイルに符号化声明を行う必要がある.声明は
    import codecs
    fp = open("test.txt",'r')
    content = fp.read()
    fp.close()
    if content[:3] == codecs.BOM_UTF8:
    content = content[3:]
    print content.decode("utf-8")
    を正規表現で表すことができるが、一般的にソースファイル符号化声明を行うには3つの方法がある:
  • "coding[:=]\s*([-\w.]+)"
  • 普通文字とUnicodeが文字列接続されているときにUnicodeDecodeError異常が放出されます.
    #coding=<encoding name>
    
    #!/usr/bin/python
    # -*- coding: <encoding name> -*-
    
    #!/usr/bin/python
    # vim: set fileencoding=<encoding name>:
    +オペレータを使用して文字列の接続を行う場合、左側は中国語文字列、タイプstr、右側はUnicode文字列、2種類の文字列が接続されている場合、pythonは左側の中国語文字列をUnicodeに変換して右側のUnicode文字列に接続し、strをUnicodeに変換する場合はシステムデフォルトのASCII符号化を使用して文字列を符号化します.UnicodeDecodeError異常が発生します.解法方法:
  • strがUnicodeに変換されたときの符号化方式を指定します.
    # coding=utf-8
    s = "  " + u"Chinese Test"
    print s
  • Unicode文字列をUTF-8符号化
    #coding=utf-8
    s = "  ".decode('gbk') + u"Chinese Test"
    提案十七:moduleを管理するために合理的なパッケージ階層を構築する本質的に各pythonファイルはモジュールであり、モジュールを使用するとコードのメンテナンス性と再利用性を向上させることができる.モジュールを管理するために、プロジェクトの階層を合理的に組織するためにパッケージ(Package)が必要です.パッケージはディレクトリですが、通常のディレクトリとは異なり、通常のpythonファイル(モジュール)のほかに__も含まれています.init__.pyファイルです.ネストできます.パッケージの構成は、
    s = "  " + u"Chinese Test".encode("utf-8")
    パッケージのモジュールを通過することができる.アクセスキャラクタは、パケット名であるアクセスを行う.モジュール名.上記ネスト構造のようにPackageの下のModule 1にアクセスするにはPackageを用いることができる.Module 1、SubpackageのModule 1にアクセスするにはPackageを使用することができる.Subpackage.Module 1パッケージのモジュールも他のモジュールにインポートできます.
  • の方法があります.
  • は、パケット
    Package/ __init__.py
    Module1.py
    Molude2.py
    Subpackage/ __init__.py
        Module1.py
        Module2.py
  • を直接インポートする.
  • 導入サブモジュールまたはサブパッケージ
    import Package
    前述パッケージのディレクトリの下にinitがあるはずである.pyファイルです.パッケージと通常のディレクトリを区別するだけでなく、モジュールレベルのimport文をプログラムパッケージレベルで表示することもできます.例として、前述の例では、importパッケージPackageの下でModule 1のクラスTestを使用する場合、_init__.pyが空の場合はフルパス
    from Package import Module1
    import Package.Module1
    from Package import Subpackage
    import Package.Subpackage
    from Package.Subpackage import Module1
    import Package.Subpackage.Module1
    を使用する必要がありますが、_init__.pyにfrom Module 1 import Test文を追加すると、次の文を直接使用してクラスTest
    from Package.Module1 import Test
    注意をインポートできます.init__.pyが空です.from Package import*を使用してパッケージPackage内のすべてのモジュールを現在の名前空間にインポートしようとする場合はできません.これは、異なるプラットフォーム間の命名規則が異なり、python解釈器が対応するプラットフォームでモジュールがどのようにインポートされたかを正確に判定できないため、_init__.pyファイル、モジュールのインポートを制御する場合は__を変更する必要がありますinit__.py
    from Package import Test
    これでいいです.バッグの使用は以下の便利さをもたらすことができる:
  • 合理的にコードを組織し、メンテナンスと使用が容易である.以下は、参照可能なpythonプロジェクト構造です.
    __all__ = ['Module1', 'Module2', 'Subpackage']
  • は、名前空間の競合を効果的に回避することができる.

  • まとめ:ここでpythonの慣用法を羅列し、これらをマスターし、熟練する必要があります.後で基礎文法に注意しなければならない点を話します.
    参考:高品質コードの作成--pythonプログラムを改善する91の提案