Pythonのマスター読み、熟練したプレイヤーのルールをやって

オリジナルリンク: https://www.jianshu.com/u/8f2987e2f9fb

プログラミングは、実際には、ビデオゲームをプレイするといくつかの類似点があります。あなたが別のゲームをプレイする前に、各ゲームごとに異なるルールを学ぶ必要がある、ゲームのルールの唯一の馴染みと柔軟な使用は、ゲームに勝つ可能性が高くなります。

プログラミングは同じで、異なるプログラミング言語も異なる、「ルール」を持っています ほとんどのビデオゲームよりも、プログラミング言語、ルールを定義することができるかどうかの定数にオブジェクト指向、小さなサポートするのに十分な大きさは、はるかに複雑です。

直接言語を適用するには、別の経験に一つの言語を取り、多くの場合、最良の結果を得ることができない場合、我々は、プログラムされた場合。これは、(生き残るためにジェダイ)PUBGを果たし続けるルールを知らずでCS(カウンターストライク)専門家のようなものだ、彼の射撃が見つかった最初の敵の前でどれもが、最も可能性が高いWANことができなかったものの、彼は落ちます敵の待ち伏せの草の中に巣インチ

ルールでのPython

Pythonは、単純な、複雑なYujue深い言語の示す兆候です。Pythonの最も重要な「オブジェクト」を取る概念的には、Pythonは次のようなすべてのルールを、覚えて作るよりも、より多くのために定義されました:

  • __ str__メソッドはオブジェクトを定義し、人間可読な名前を返すために、STR()関数を使用することができます

  • __ next__定義__ iter__方法およびオブジェクトは、反復ループであってもよいです

  • __ bool__オブジェクト定義された方法で、ブールを行うことは、カスタム・ロジックを使用して決定します

... ...

ルールに精通し、これらのルールに適応するための独自のコードを作る、あなたは私たちが作業を完了し、より効率的に、より本格的なコードを書くことができます。次は、適応規則についての話を見てみましょう。

ケース:2つの走行データから人物のリストを取得します。

ある日、メインのニュージーランドのアウトバウンド旅行会社では、ビジネスの同僚は突然私を見つけるために興奮して走った、彼は二つの重要なデータがあるように、パートナーから言いました:

  1. すべては「プーケット、タイ」人員と連絡先情報となっています

  2. すべての人事や連絡先情報の「ニュージーランド」に行ったことがあります

次のように、データを使用してJSON形式:

# 去过普吉岛的人员数据
users_visited_puket = [
    {"first_name": "Sirena", "last_name": "Gross", "phone_number": "650-568-0388", "date_visited": "2018-03-14"},
    {"first_name": "James", "last_name": "Ashcraft", "phone_number": "412-334-4380", "date_visited": "2014-09-16"},
    ... ...
]

# 去过新西兰的人员数据
users_visited_nz = [
     {"first_name": "Justin", "last_name": "Malcom", "phone_number": "267-282-1964", "date_visited": "2011-03-13"},
     {"first_name": "Albert", "last_name": "Potter", "phone_number": "702-249-3714", "date_visited": "2013-09-11"},
     ... ...
]

名前、電話番号、タイムトラベル最初と最後の4つのフィールドを持っている各データ。このデータに基づいて、学生がビジネスを提示仮定(不当の音):「プーケットに行ったことがある人は、ニュージーランドの観光に行くべきまた、我々はそれらを見つける必要があり、このデータに非常に興味があります。プーケットに行ったことが、ニュージーランドに行ったことがない人、彼らに製品を販売することを目標。

ブルートフォースへの最初の試み

生データと明確な需要で、次の質問には、コードの記述方法です。ブルートフォースに依存している、私はすぐに最初のプログラムを書きました:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,
互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def find_potential_customers_v1():
    """找到去过普吉岛但是没去过新西兰的人
    """
    for puket_record in users_visited_puket:
        is_potential = True
        for nz_record in users_visited_nz:
            if puket_record['first_name'] == nz_record['first_name'] and \
                    puket_record['last_name'] == nz_record['last_name'] and \
                    puket_record['phone_number'] == nz_record['phone_number']:
                 is_potential = False
                 break

         if is_potential:
             yield puket_record

そこに独自に「ユーザーID」などを特定全く生データではないので、我々は唯一の「名前と電話番号が同一である」ことができるので、裁判官の判断基準と同一人物ではありません。

循環を介してfind_potential_customers_v1機能、プーケットに行ったことがあるすべての人の最初のトラバーサル、あなたはニュージーランドのレコードでの完全一致レコードを見つけることができない場合は、その後、ニュージーランドの人々をトラバースは、返すように「潜在顧客」としてそれを置きます。

この機能は、タスクを完了することができますが、私はあなたが見つけることができると言うことは必要だと思いますが。これは非常に深刻なパフォーマンス上の問題を抱えています。各レコードは、プーケットに行ったことがあるために、我々はすべての試合を見つけようと、レコードに対するすべてのニュージーランドへのアクセスを通過する必要があります。アルゴリズムの時間計算量は、エントリ数がニュージーランドに多くのことをアクセスする場合、それは非常に長い時間がかかる実行し、ひどいO(n個の* m)をです。

内側のループのパフォーマンスを最適化するために、我々は、線形検索の部分に一致するコストを削減する必要があります。

最適化された関数のセットを使用してみてください

あなたは、Pythonを知っている場合は、辞書とオブジェクトのコレクションでは、確かにPythonのを知っているハッシュテーブル(ハッシュテーブル)の実装に基づいています。事を分析することは、コレクション内の平均時間複雑ではありませんOは、(1)、非常に高速です。

したがって、上記の機能のために、我々は最初の検索がすぐに試合の一部になることができた後、ニュージーランドのコレクションを初期化するためのレコードにアクセスしようとすることができ、全体の時間計算機能は、O(N + M)に変更することができます。

のは、新機能を見てみましょう:

def find_potential_customers_v2():
    """找到去过普吉岛但是没去过新西兰的人,性能改进版
    """
    # 首先,遍历所有新西兰访问记录,创建查找索引
    nz_records_idx = {
        (rec['first_name'], rec['last_name'], rec['phone_number'])
        for rec in users_visited_nz
    }

     for rec in users_visited_puket:
         key = (rec['first_name'], rec['last_name'], rec['phone_number'])
         if key not in nz_records_idx:
             yield rec

オブジェクトのコレクションを使用した後、新しい機能が画期的なのスピードの飛躍の古いバージョンと比較します。「アプリケーションのパフォーマンスを向上させるためにコレクションを使用する方法、」:しかし、この問題の最適化は、それ以外の場合は記事のタイトルに変更する必要があり、ポイントではありません。

問題を再考
者は、問題の抽象的な本質について考えてみましょう。まず第一に、我々は、コンテナA(プーケットアクセスレコード)多くのことをインストールして、その後、別のBが(記録へのニュージーランドへのアクセス)、そして平等ルール定義のコンテナロットインストールたちを与えた:「同じ名前と電話を」このルールに基づいて、AとBの間の最後の追求「に設定の違い」に等しいです

あなたは、コレクション内のPythonに特に精通していない場合、私はもう少し紹介します。私たちは二組のAとBを持っている場合は、私たちは直接、そのような数学的な表現ABを使用して2つのセットの間の差を計算することができます。

>>> a = {1, 3, 5, 7}
>>> b = {3, 5, 8}
# 产生新集合:所有在 a 但是不在 b 里的元素
>>> a - b
{1, 7}

したがって、計算の「すべては、プーケットに行ってきましたが、誰がニュージーランドにされていない、」実際に操作を差分のセットです。それでは、どのようにそれを行くためにゲームを設定したルールのセットに私たちの問題を置くために、行うには?

ゲームのルールのセットを使用します

私たちが何かをしたい場合はPythonでは、コレクションや辞書に取り付けた、特定の基本的な条件が満たされる:「このことは、(ハッシュ可能)ハッシュ化されなければなりません。」「ハッシュ可能」とは何ですか?

例えば、辞書などの全ての可変オブジェクトの内部Pythonは、それがハッシュ不可能です。あなたは辞書にセットを置くしようとすると、このようなエラーが発生します。

>>> s = set()
>>> s.add({'foo': 'bar'})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

VisitRecord:あなたが私たちの問題を解決するために、コレクションを使用したいのであれば、我々は最初に私たち自身の「ハッシュ可能」オブジェクトを定義する必要があります。カスタムオブジェクトを作るためにはハッシュ可能となり、実行する唯一のものは、オブジェクトの__ hash__メソッド定義です。

class VisitRecord:
    """旅游记录
    """
    def __init__(self, first_name, last_name, phone_number, date_visited):
        self.first_name = first_name
        self.last_name = last_name
        self.phone_number = phone_number
        self.date_visited = date_visited

良好なハッシュアルゴリズムは、このように「ハッシュ衝突」デフォルトの確率最大限に減少させる、異なるオブジェクト間の値のみをさせなければならない、そのメモリアドレスからの全てのPythonオブジェクトのハッシュ値。

この問題では、我々は__方法、それはVisitRecordクラスのソースとして(姓、名、電話)タプルハッシュ値を使用し__hashカスタムオブジェクトにする必要があります。

def __hash__(self):
    return hash(
        (self.first_name, self.last_name, self.phone_number)
    )

あなたはhash__方法__カスタマイズが完了したら、VisitRecordインスタンスは通常、コレクションに配置することができます。しかし、それは十分ではなかった、先に述べたアルゴリズムを差分の正常な動作を可能にするために、我々はまた、特別な方法__ eq__を実装する必要があります。

__ 2つのオブジェクトが等しいかどうかを決定する際にPythonの特別なメソッドが呼び出されるeq__。デフォルトでは、それだけでまったく同じメモリアドレスの独自の及びその他の目的で、それはtrueを返しますです。しかし、ここでは、我々は両方とも同じであるハッシュ値VisitRecordオブジェクトを再利用し、彼らは同じとみなされます。

def __eq__(self, other):
    # 当两条访问记录的名字与电话号相等时,判定二者相等。
    if isinstance(other, VisitRecord) and hash(other) == hash(self):
        return True
    return False

適切なデータモデリングを完了した後、差分後の動作は、当然の結果とみなされます。機能の新バージョンでは、操作を完了するために、コードの一行だけを必要とします。

def find_potential_customers_v3():
    return set(VisitRecord(**r) for r in users_visited_puket) - \
        set(VisitRecord(**r) for r in users_visited_nz)

ヒント:(等しくない使用して決定)、あなたはメソッドを使用している場合、__ __ neのPythonの2、そしてさらに__ eq__方法では、あなたはまた、カスタムクラスにする必要があります。

コードのデータクラスを単純化する使用

物語はここで終わりではありません。上記のコードでは、我々は、手動で自分のデータクラスVisitRecordを定義し、実現INITEQおよび他の初期化方法。しかし、実際にはより容易なアプローチがあります。

そのため、あまりにも一般的なPythonでこの要求定義のデータ型なので、3.7バージョンで、あなたはこの種の仕事を簡素化するために設計されたデータクラスの標準ライブラリモジュールが追加されます。

特性のデータクラスが提供されている場合、我々のコードは、最終的には、以下に減らすことができます。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,
互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
@dataclass(unsafe_hash=True)
class VisitRecordDC:
    first_name: str
    last_name: str
    phone_number: str
    # 跳过“访问时间”字段,不作为任何对比条件
    date_visited: str = field(hash=False, compare=False)


def find_potential_customers_v4():
    return set(VisitRecordDC(**r) for r in users_visited_puket) - \
        set(VisitRecordDC(**r) for r in users_visited_nz)

コードの未満10行する限り、任意の汚い仕事をしてせずに作業を完了します。

概要

問題を解決した後、ちょっとした要約を行うことができます。この問題に対処するには、我々は3つのシナリオの合計を使用しました。

  1. 設定されたルールに沿って、通常の2サイクルの結果を使用してスクリーニング

  2. ハッシュテーブル構造(セットオブジェクト)がインデックスを作成すると、処理効率を向上させます

  3. ルールを使用して、カスタムオブジェクトにデータを変換するには、直接設定操作

なぜ第三の方法は、最初の2つのより良いでしょうか?

まず、パフォーマンスの問題、最初のオプションはあまりにも明白であるので、すぐに放棄されます。そして、2番目のプログラム、それ?実際には、ないプログラム2つの明らかな欠点、それについて考えてみよう。さらには、第三の態様に比べて、性能とメモリ使用量にも後者よりわずかに強いがあるかもしれない以下のカスタム・オブジェクト・プロセスからです。

完全に翻訳1:あなたはJavaなど他の言語の中に2つのプログラムコードを置けばしかし、もう一度考えて、それは必須ではない1を行うことができますか?言い換えれば、それは直接高効率、コードですが、それは完全に世界のルールを有効に利用することはありませんが、それからの利益を最大化するために、Pythonのが用意されています。

あなたは「ルール」の問題を実現したい場合、つまり事実そのもの「Pythonはビルトイン構造のセットがあり、そのようなコレクションとして4つの事業との違いがあることができます」。ルールに一致した後、プログラムIIIは、これらの利点書き込まれ、次のコードを有します。

  • データ・モデルの後に他のメソッドを定義することがより便利であり得ます

  • 要件が変更された場合は、差額を計算する逆の操作を行い、操作の交差点は非常に簡単です

  • データクラスロジックのセットを理解した後に、よりシンプルかつ明確なコードの他のバージョンより

  • あなたは、このような__ eq__をカバーするだけで継承されたVisitRecord法「だけでもと同じ名前のレコードを持っている」と同等のルールを変更したい場合

私たちは、他のルールにどのような影響を与えますか

フロントでは、我々はより少ないコードでより多くを書くために、「一連のルール」を使用する方法について話をする偉大な長さを取りました。また、Pythonの世界では、他の多くのルールがあります。あなたはこれらのルールを習得することができた場合、我々はコードより簡潔精錬を行うために、実際に沿ってPythonのAPIを設計することができます。

以下の2つの具体例です。

文字列フォーマット化オブジェクトは、format__ __ん使用します

あなたは次のように、さまざまな方法のカスタムオブジェクトの文字列表現を定義する必要がある場合:

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def get_simple_display(self):
        return f'{self.name}({self.age})'

    def get_long_display(self):
        return f'{self.name} is {self.age} years old.'

piglei = Student('piglei', '18')
# OUTPUT: piglei(18)
print(piglei.get_simple_display())
# OUTPUT: piglei is 18 years old.
print(piglei.get_long_display())

だから、(このget_xxx_displayを増やすために追加の方法)に加えて、あなたもそのオブジェクトが文字列の標準ルールになっているので、Studentクラスの__ format__方法をカスタマイズしようとすることができます。

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __format__(self, format_spec):
        if format_spec == 'long':
            return f'{self.name} is {self.age} years old.'
        elif format_spec == 'simple':
            return f'{self.name}({self.age})'
        raise ValueError('invalid format spec')


piglei = Student('piglei', '18')
print('{0:simple}'.format(piglei))
print('{0:long}'.format(piglei))

__getitemをスライス定義オブジェクトを使用して__

あなたは物事を保持できる容器の種類を設計する場合は、おそらくそれのための他の方法「最初のN個のオブジェクトを取得する」、「空である」を定義します:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,
互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class Events:
    def __init__(self, events):
        self.events = events

    def is_empty(self):
        return not bool(self.events)

    def list_events_by_range(self, start, end):
        return self.events[start:end]

events = Events([
    'computer started',
    'os launched',
    'docker started',
    'os stopped',
])

# 判断是否有内容,打印第二个和第三个对象
if not events.is_empty():
    print(events.list_events_by_range(1, 3))

しかし、これが最善のアプローチではありません。Pythonは私たちのために、オブジェクトルールのセットを提供してきましたので、私たちは独自の追加メソッドを定義するためのコードなどの他の言語のOO(オブジェクト指向)で書くことが好きではありません。私たちは、より良い選択肢があります。

class Events:
    def __init__(self, events):
        self.events = events

    def __len__(self):
        """自定义长度,将会被用来做布尔判断"""
        return len(self.events)

    def __getitem__(self, index):
        """自定义切片方法"""
        # 直接将 slice 切片对象透传给 events 处理
        return self.events[index]

# 判断是否有内容,打印第二个和第三个对象
if events:
    print(events[1:3])

新しい文言は、より良い、通常のPythonの世界では、APIとより簡潔に収まるように古いコードと比較します。

ルールを適応する方法について、より良いPythonのコードを記述します。「 - 美しい分かりやすいコードのためのベストプラクティスを超えPEP8」レイモンドヘッティンガーはPyCon 2015に非常に素晴らしいスピーチを持っていました。あなたが見ていない場合、これは、長い「PyConビデオTOP5」の私の個人的なリストにスピーチで来て、私は強くあなたはそれを再び見に行くことをお勧め

ヒント:Pythonのオブジェクトモデルより包括的なルールは少し読みにくいが、読む価値が、公式文書に記載されています。

概要

Pythonの世界では、これらのルールの範囲をカバーする非常に複雑なルールのセット「平等のためのオブジェクトとオブジェクト」には、「オブジェクトを、小型で誰素晴らしいです誰オブジェクト」ようにとしています。それらのほとんどは、達成するために、「二重のアンダースコア方式の__のxxx__」を再定義することによって必要とされています。

あなたはこれらのルールに精通している、そして日常のコーディングでそれらを利用する場合は、私たちはAPIのPythonの理念に沿って、より多くを設計するために、より効率的に問題を解決するのに役立ちます。ここではいくつかの重要なポイントは、この記事にまとめられています。

  • 常にこのような問題が違いを設定要件のセットを解くことができるかどうかなど、オリジナルの要件の抽象分析を行うことを忘れないでください

  • あなたがオブジェクトのコレクションに入れた場合、必要なカスタムオブジェクトとhash__ __ __ eq__方法

  • 性能を決定するの__ hash__法(衝突の確率)、__オブジェクト間eq__等しい決定ロジック

  • あなたはあまり多くのコードを書くことができますデータクラスモジュールを使用します

  • その中で定義された文字列の書式設定方法を使用する__ format__代替

  • むしろ、達成しようとするよりも、コンテナクラスオブジェクト__ len__、__ getitem__方法で使用

おすすめ

転載: blog.csdn.net/qdPython/article/details/102724896