Pythonの使用を禁止しているのに、なぜPythonの柔軟性が必要なのですか?

こんにちは 次のタスクがありました。大量のデータを解析してクラスに整理し、後でデータベースにロードする必要がありました。 複雑なことは何もないように思えますが、この日は食べるのを忘れてしまいました。



画像






もちろん、大量のデータがありましたが、タスクを複雑にすることはありませんでした。サイトのさまざまなコーナーで同じ要素を見つけることができるという事実によって複雑になりました。 これらのデータは、ソーシャルネットワーク上のアカウントと比較できます。 同じアカウントはどこにでもそのマークを残すことができます-別のページにいいね!を付け、どこにでもコメントを書き、別の人の壁に何かを掛けます そして、これらはすべてプログラム内の同じオブジェクトであり、どのような方法でも複製しないことが必要です。 すべてが単純なようです。この要素がすでに見つかっているかどうかを確認してください-それだけです。 しかし、これはい、これは労働ではありません。 はい、そしてPythonの哲学に反します。 すでに存在する要素の作成を単に禁止するか、単に作成しない、すべての初期化を無視し、内部コンストラクターが既存の要素を返す美しいソリューションが必要でした。



例を挙げましょう。 たとえば、エンティティがあります。



class Animal: def __init__(self, id): self.id=id
      
      





そして、そのようなエンティティはそれぞれ独自のIDを持っています。



その結果、異なる場所で2つの同一のエンティティを見つけ、2つの完全に同一のオブジェクトを作成します。 最初に行うことは、ある種のオブジェクトストレージを追加することです。



 class Animal: __cache__=dict() def __init__(self, id): self.id=id
      
      





pythonの新しいオブジェクトが__new__クラスの関数で作成されます。この関数は新しく作成されたオブジェクトを返す必要があり、要素を作成する動作を再定義するために掘る必要があります。



 class Animal: __cache__=dict() def __new__(cls, id): if not id in Animal.__cache__: Animal.__cache__[id]=super().__new__(cls) return Animal.__cache__[id] def __init__(self, id): self.id=id
      
      





それがすべてであるように、問題は解決されます。 最初の20分だと思いました。 プログラムを展開してクラスを増やすと、次のようなエラーが発生し始めました: __init __()required N position argument



この問題により、検索でグーグル検索することを余儀なくされました。 はい、判明しました。 __new__メソッドに不必要に入らないように言われ、Factoryパターンが代替手段を提供しました。



要するに、Factoryパターンは、オブジェクトの作成を管理するスペースを割り当てることです。 Pythonについては、彼らはこのを提案しました



 class Factory: def register(self, methodName, constructor, *args, **kargs): """register a constructor""" _args = [constructor] _args.extend(args) setattr(self, methodName,apply(Functor,_args, kargs)) def unregister(self, methodName): """unregister a constructor""" delattr(self, methodName) class Functor: def __init__(self, function, *args, **kargs): assert callable(function), "function should be a callable obj" self._function = function self._args = args self._kargs = kargs def __call__(self, *args, **kargs): """call function""" _args = list(self._args) _args.extend(args) _kargs = self._kargs.copy() _kargs.update(kargs) return apply(self._function,_args,_kargs)
      
      





Factoryクラスのメソッドを使用してのみオブジェクトを作成できます。 絶対に使用してオブジェクトを直接作成することはできませんが、 一般的に、このようなソリューションは正しいかもしれませんが、私はそれが好きではなかったので、自分のコードでソリューションを探すことにしました。



作成プロセスの少しの研究は私に答えを与えました。 オブジェクトの作成(簡単に言うと)は次のとおりです。最初に、クラスとコンストラクターのすべての引数が渡される__new__メソッドが呼び出され、このメソッドはオブジェクトを作成して返します。 後で、オブジェクトが属するクラスの__init__メソッドが呼び出されます。



抽象コード:



 def __new__(cls, id, b, k, zz): return super().__new__(cls) def __init__(self, id, b, k, zz): # anything self.id=id obj=Animal.__new__(Animal, 1, 2, k=3, zz=4) obj.__class__.__init__(obj, 1, 2, k=3, zz=4)
      
      





問題は次のアクションで明らかになりました。 たとえば、Catクラスを追加します



 class Cat(Animal): data="data" def __init__(self, id, b, k, zz, variable, one_more_variable): # anything pass
      
      





ご覧のとおり、クラスのコンストラクターは異なります。 id = 1のAnimalオブジェクトをすでに作成していることを想像してください。 後でid = 1のCat要素を作成します。



id = 1のAnimalクラスのオブジェクトは既に存在しているため、物事のロジックにより、Catクラスのオブジェクトは作成しないでください。 一般に、彼はこれを行いませんが、__ init__が異なる数の引数を渡したという事実でエラーを完了します。



ご理解のとおり、彼はCatクラスの要素を作成しようとしますが、後でAnimalクラスのコンストラクターを呼び出します。 間違ったコンストラクタを呼び出すだけでなく、本当に悪い結果は、id = 1でAnimalを再度作成した場合でも、同じオブジェクトのコンストラクタが再度呼び出されることです。 そして、おそらく彼はすべてのデータを上書きし、望ましくないアクションを起こすでしょう。



良くない また、戻ってオブジェクトを生成するためのファクトリを作成することも理にかなっています。

しかし、最も柔軟で美しい言語であるPythonで記述しているため、譲歩する必要があります。



判明したように、解決策があります:



 class Animal: __cache__=dict() __tmp__=None def __fake_init__(self, *args, **kwargs): self.__class__.__init__=Animal.__tmp__ Animal.__tmp__=None def __new__(cls, id): if not id in Animal.__cache__: Animal.__cache__[id]=super().__new__(cls) else: Animal.__tmp__=Animal.__cache__[id].__class__.__init__ Animal.__cache__[id].__class__.__init__=Animal.__fake_init__ return Animal.__cache__[id] def __init__(self, id): self.id=id
      
      





コンストラクター呼び出しを無効にすることは不可能でした。__new__が実行された後、作成された(またはこの場合のようにではない)オブジェクトのクラスから__init__関数の呼び出しが疑いなく行われました。 解決方法は1つしかありません-作成されたオブジェクトのクラスの__init__を置き換えることです。 クラスコンストラクターを失わないために、変数に保存し、代わりに偽のコンストラクターをスリップしました。その後、オブジェクトが「作成」されたときに呼び出されました。 しかし、偽のコンストラクタは空ではありません。古いコンストラクタをその場所に戻すことにコミットしているのは彼です。



最終的に私は非常に間違っているかもしれません、リストやタプルなどの反復型から継承する場合にのみ__new__に触れることができると言っている公式のPython開発者コミュニティでさえ、私のコードは警告に反していることに気づきました。 しかし、私には思えます。時には、良識の範囲を超えて、後で冷静に書くことができるだけの価値がある場合もあります。



 an1=Animal(1) an2=Animal(1) cat1=Cat(1)
      
      





問題を心配する必要はありません。



ご清聴ありがとうございました!



All Articles