クラスを曞くのをやめる

G +のプロフィヌルのJack Didrichの写真 オブゞェクトがクラスであっおはならないずいう兆候-オブゞェクトが2぀しかなく、そのうちの1぀が初期化__init__である堎合。 これを芋るたびに、「おそらく1぀の関数が必芁なだけだ」ず考えおください。



蚘述されたクラスからむンスタンスを1぀だけ䜜成し、それを1回だけ䜿甚し、すぐに砎棄するたびに、次のこずを考える必芁がありたす。 簡単に、もっず簡単にできたす」



Python蚀語の䞻芁な開発者の1人であるJack Didrichによるレポヌトの翻蚳。 レポヌトは 2012幎3月9日にPyCon USカンファレンスで配信されたした。



皆さんは、おそらく䜕床もZen Pythonを読んだこずがありたす。 以䞋にいく぀かのポむントを瀺したす。







このテキストはティムピヌタヌズによっお曞かれたした。 圌はあなたず私より賢い。 ゜ヌトアルゎリズムの名前を知っおいる人は䜕人いたすか こちらがZen Pythonを曞いた人です。 そしお、すべおのポむントはこう読みたす。「難しいこずをしないでください。 たさにそれを行う。」これが議論されるものです。



だから、たず第䞀に、それを難し​​くしないでください。 クラスは非垞に耇雑であるか、非垞に耇雑な堎合がありたす。 しかし、私たちはそれをさらに困難にしおいたす。 したがっお、このレポヌトでは、少しのコヌドを読み、間違った方向に進んでいるこずに気付く方法ず、戻る方法を孊びたす。



私の職堎では、同僚に次のように䌝えおいたす。「私はコヌドが嫌いです。補品ではできるだけ小さくしたいのです。」私たちはコヌドではなく機胜を販売しおいたす。 私たちの顧客はコヌドのためではなく、幅広い機胜のためです。 コヌドが削陀されるたびに、それは良いこずです。 私たちは4人で、昚幎は補品の行数のカりントを停止したしたが、新しい機胜を導入し続けおいたす。



クラス





このレポヌトから、最初にこのコヌドを芚えおおく必芁がありたす。 これは、自然界で芋られる最倧のクラス乱甚です。



class Greeting(object): def __init__(self, word='Hello'): self.word = word def greet(self, name): return '%s, %s!' % (self.word, name) >>> greeting = Greeting('Hola') >>> greeting.greet('Jorge') Hola, Jorge!
      
      







クラスのように芋えたすが、これはクラスではありたせん。 名前は名詞、「あいさ぀」です。 匕数を取り、__ init__に保存したす。 はい、クラスのように芋えたす。 圌には、オブゞェクトの状態を読み取り、クラスのように他のこずを行うメ゜ッドがありたす。 以䞋に、このクラスの䜿甚方法を瀺したす。Greetingsのむンスタンスを䜜成しおから、このGreetingsを䜿甚しお他のこずを行いたす。



しかし、これはクラスではないか、クラスであっおはなりたせん。 この兆候は、メ゜ッドが2぀しかないこずであり、そのうちの1぀は初期化__init__です。 これを芋るたびに、「おそらく1぀の関数が必芁なだけだ」ず考えおください。



蚘述されたクラスからむンスタンスを1぀だけ䜜成し、それを1回だけ䜿甚し、すぐに砎棄するたびに、次のこずを考える必芁がありたす。 簡単に、もっず簡単にできたす」



 def greet(name): ob = Greeting('') print ob.greet(name) return
      
      







この関数は4行のコヌドで構成されおいたす。 そしお、次の2行で同じこずができたす。



 def greet(greeting, name): return '%s, %s!' % (greeting, name) import functools greet = functools.partial(greet, '') greet('')
      
      







垞に同じ最初の匕数で関数を呌び出す堎合、暙準ラむブラリにはツヌルがありたす functools.partial。 䞊蚘のコヌドを芋おください匕数を远加するず、結果を耇数回呌び出すこずができたす。



ITの孊䜍を持っおいる人がどれだけいるのかわかりたせん。 私は次のような抂念を教えたした



-暩力の分離

-コヌド接続の削枛

-カプセル化

-販売の分離



卒業しお以来、これらの甚語を15幎間䜿甚しおいたせん。 これらの蚀葉を聞いお、あなたはだたされおいたす。 これらの甚語自䜓は必芁ありたせん。 それらが䜿甚される堎合、人々は䌚話を劚害するだけの完党に異なるものを意味したす。



䟋パンツタヌン...





あなたの倚くは、日垞業務でサヌドパヌティのラむブラリを䜿甚しおいたす。 他の人のコヌドを䜿甚する必芁があるたびに、最初にするこずはそれを読むこずです。 結局のずころ、そこにどのような品質があるか、テストなどがあるかどうかは䞍明です。 電源を入れる前にコヌドを確認する必芁がありたす。 コヌドを読むのが難しい堎合がありたす。



サヌドパヌティのAPIラむブラリ、それをShaurMailず呌びたしょう。1぀のパッケヌゞ、22のモゞュヌル、20のクラス、660行のコヌドが含たれおいたす。 補品に含める前に、これらすべおを読む必芁がありたした。 しかし、それは圌らの公匏APIであったため、それを䜿甚したした 。 APIの曎新が来るたびに、差分を確認する必芁がありたした。䜕が倉曎されたのかわからなかったためです。 パッチを送信したした-アップデヌトに衚瀺されたしたか



660行のコヌド、20クラスは、プログラムが電子メヌルアドレスのリスト、手玙のテキストを提䟛し、配信されなかった手玙ず賌読解陀した人を芋぀けるだけでよい堎合、少し倚くなりたす。



クラス虐埅ずは䜕ですか 倚くの堎合、人々は将来、䜕かが必芁になるず考えたす。 必芁なずきにすべおを曞きたす。 ShaurMailラむブラリにはShaurHashモゞュヌルがあり、次の2行のコヌドが含たれたす。



 class ShaurHash(dict): pass
      
      







誰かが蟞曞のアドオンが埌で必芁になるず決めたした。 必芁ではありたせんでしたが、コヌドのどこにでも最初の行がありたした。



 my_hash = ShaurMail.ShaurHash.ShaurHash(id='cat') d = dict(id='cat') d = {'id': 'cat'}
      
      







コヌドの2行目ず3行目-誰も説明する必芁はありたせん。 しかし、そこでは、このマントラ「ShaurMail-ShaurHash-ShaurHash」がどこでも繰り返されたした。 「Shaur」ずいう単語の3回の繰り返しは、過剰の別の兆候です。 繰り返しからすべおの害たで。 ナヌザヌに「Shaur」を3回曞くように匷制するこずでナヌザヌを困らせたす。 これは実際の䌚瀟名ではなく、架空のものです。



それから圌らはこの男を解雇し、圌が䜕をしおいるかを知っおいる人を雇った。 APIの2番目のバヌゞョンは次のずおりです。



 class API: def __init__(self, key): self.header = dict(apikey=key) def call(self, method, params): request = urllib2.Request( self.url + method[0] + '/' + method[1], urllib.urlencode(params), self.header ) try: response = json.loads(urllib2.urlopen(request).read()) return response except urllib2.HTTPError as error: return dict(Error=str(error))
      
      







このコヌドには660行、このコヌドには15行がありたす。このコヌドが行うこずはすべお、暙準ラむブラリのメ゜ッドを䜿甚しおいたす。 数秒で簡単に党䜓を読むこずができ、䜕をしおいるのかすぐに理解できたす。 ちなみに、20行のテストもありたした。 曞き方は次のずおりです。 圌らがAPIを曎新したずき、私は文字通り数秒で倉曎を読むこずができたした。



しかし、ここで問題に気づくこずができたす。 クラスには2぀のメ゜ッドがあり、そのうちの1぀は__init__です。 著者はこれを隠したせんでした。 2番目の方法は、呌び出し、「呌び出し」です。 このAPIの䜿甚方法は次のずおりです。



 ShaurMail.API(key=' ').call(('mailing', 'statistics'), {'id': 1})
      
      







行が長いため、゚むリアスを䜜成しお䜕床も呌び出したす。



 ShaurMail.request = ShaurMail.API(key=' ').call ShaurMail.request(('mailing', 'statistics'), {'id': 1})
      
      







このクラスを関数ずしお䜿甚しおいるこずに泚意しおください。 それがあるはずです。 これが衚瀺された堎合、ここではクラスが必芁ないこずを知っおください。 そこで、私は圌らにAPIの3番目のバヌゞョンを送信したした。



 ShaurMail_API = url = 'https://api.shaurmail.com/%s/%s' ShaurMail_API_KEY = ' ' def request(noun, verb, **params): headers = {'apikey': ShaurMail_API_KEY} request = urllib2.Request(ShaurMail_API % (noun, verb), urllib.urlencode(params), headers) return json.loads(urllib2.urlopen(request).read())
      
      







䜿甚するモゞュヌルにファむルを挿入したため、プロゞェクトにファむルを䜜成したせん。 15文字列APIが実行したすべおの凊理ず、660文字列APIが実行したすべおの凊理を実行したす。



これが私たちが始めた堎所であり、私たちが来た目的です







䜿いやすく、曞きやすく、䜕が起こっおいるのかを理解する必芁はありたせん。



暙準ラむブラリ





Java蚀語から来た人は誰でも、分類に名前空間が必芁であるず考えるかもしれたせん。 これは真実ではありたせん。 名前の䞀臎を防ぐために必芁です。 スペヌスの深い階局がある堎合、誰にも䜕も提䟛したせん。 ShaurMail.ShaurHash.ShaurHashは、人々が芚えお曞く必芁がある単なる远加の単語です。



Python暙準ラむブラリでは、モゞュヌルの名前を芚えおいるか、ドキュメントを参照する必芁があるため、名前空間は非垞に浅くなっおいたす。 チェヌン、怜玢するパッケヌゞ、そのパッケヌゞ、次のパッケヌゞ、およびその䞭のモゞュヌルの名前を把握する必芁がある堎合は、䜕も良いこずはありたせん。 モゞュヌルの名前を知る必芁があるだけです。



残念なこずに、ここにコヌドの䟋を瀺したす。同じ眪がここにありたす。



 services.crawler.crawlerexceptions.ArticleNotFoundException
      
      







2行のモゞュヌル、䟋倖クラス、および「パス」があるパッケヌゞ。 この䟋倖を䜿甚するには、「クロヌラヌ」を2回、「䟋倖」ずいう単語の2倍蚘述する必芁がありたす。 ArticleNotFoundExceptionずいう名前は繰り返されたす。 そうしないでください。 䟋倖を呌び出す堎合は、EmptyBeer、BeerError、BeerNotFoundにしたすが、BeerNotFoundErrorはすでにたくさんありたす。



暙準ラむブラリの䟋倖を単玔に䜿甚できたす。 それらは誰にでも明らかです。 特定の状態をキャッチする必芁がない限り、LookupErrorは問題ありたせん。 あなたがメヌルでたわごずを受け取った堎合、あなたはそれをただ読たなければならないので、䟋倖が䜕ず呌ばれるかは関係ありたせん。



さらに、コヌド内の䟋倖は通垞、raiseおよびexceptの埌にあり、誰もがこれらが䟋倖であるこずをすぐに理解したす。 したがっお、名前に「䟋倖」を远加する必芁はありたせん。



Python暙準ラむブラリにも錆びた郚分がありたすが、コヌド線成の非垞に良い䟋です。







パッケヌゞ内の10個のファむルは倧量ですが、これはラむブラリに远加されたいく぀かのサヌドパヌティプロゞェクトが原因で、2぀のファむルのみのパッケヌゞがありたした。 新しい䟋倖を䜜成するこずにした堎合、暙準ラむブラリは1200行のコヌドに察しお1぀の䟋倖を芁するため、よく考えおください。



私は原則ずしおクラスに反察ではありたせん。 クラスが必芁です-倉化するデヌタず関連する関数がたくさんあるずき。 ただし、これは日垞業務ではたれにしか発生したせん。 暙準ラむブラリを定期的に䜿甚する必芁があり、すでに適切なクラスがありたす。 それらはすでにあなたのために曞かれおいたす。



Pythonラむブラリの唯䞀の䟋倖はheapqモゞュヌルです。 ヒヌプキュヌ、ヒヌプキュヌは、垞に゜ヌトされる配列です。 heapqモゞュヌルには倚数のメ゜ッドがあり、それらはすべお同じヒヌプで動䜜したす。 最初の匕数は垞に同じたたです。぀たり、クラスは本圓にそれを頌みたす。



 heapify(data) pushleft(data, item) popleft(data) pushright(data, item) popright(data)
      
      





など



heapqを䜿甚する必芁があるたびに、このクラスの実装をツヌルキットから取埗したす。



 class Heap(object): def __init__(self, data=None, key=lambda x: None): self.heap = data or [] heapq.heapify(self.heap) self.key = key def pushleft(self, item): if self.key: item = (self.key(item), item) heapq.pushleft(self.heap, item) def popleft(self): return heapq.popleft(self.heap)[1]
      
      







クラスは雑草のように成長する





PythonのOAuthステヌタスは重芁ではありたせん。 繰り返したすが、サヌドパヌティのラむブラリがあり、プロゞェクトで䜿甚する前に、それらを読む必芁がありたす。



GoogleのURLレデュヌサヌを䜿甚しようずしたした。URLを取埗しお短瞮する必芁がありたした。 Googleには、10,000行のコヌドを含むプロゞェクトがありたす。 115モゞュヌルず207クラス。 Google +でre責を曞きたしたが、 それを芋た人はほずんどいたせんでした。GuidoVan Rossum-およそPer。はコメントしたした「私はGoogleのAPIコヌドに責任を負いたす。」10,000行のコヌド- -ShaurMaleのようなもの。 たずえば、ここには、他のクラスが継承するFlowクラスがありたす。



 class Flow(object): pass class Storage(object): def put(self, data): _abstract() def get(self): _abstract() def _abstract(): raise NotImplementedError
      
      







圌は空です。 しかし、圌には独自のモゞュヌルがあり、それを継承するクラスを読み取るたびに、倖出し、そのファむルをチェックし、そのクラスが空であるこずを確認する必芁がありたす。 誰かが未来を芋぀め、「今から3行のコヌドを曞くので、将来これらの3行が倉わらないようにする」ず決めたした。そしお、圌のラむブラリを読むすべおの人から時間をかけたした。 Storageクラスもあり、ほずんど䜕もしたせん。 暙準の䟋倖を䜿甚しお゚ラヌを正しく凊理したすが、゚むリアスを䜿甚するため、コヌドを読んで動䜜を確認する必芁がありたす。



OAuth2を実装するのに1週間かかりたした。 1䞇行のコヌドを読むのに数日かかりたした。その埌、他のラむブラリを探し始めたした。 python-oauth2が芋぀かりたした。 これはpython-oauthの2番目のバヌゞョンですが、OAuth2の操䜜方法が実際にはわかりたせん。 ただし、このラむブラリはGoogleよりわずかに優れおいたす540行ず15クラスのみです。



さらに簡単に曞き盎しお、 python-foauth2ず呌びたした 。 135行のコヌドず3぀のクラス、そしおそれはただたくさんありたす。私はそれを十分にリファクタリングしたせんでした。 これら3぀のクラスの1぀を次に瀺したす。



 class Error(Exception): pass
      
      







恥



人生





ゎスパヌグラむダヌキャノン 最埌の䟋。 ConwayのLifeゲヌムは 、名前がわからなくおも芋たこずある人はすべおいたす。 垂束暡様のフィヌルドがあり、あなたが考える各動きは各セルに隣接しおおり、それらに応じお、それは生きおいるか死んでいるでしょう。 そしお、グラむダヌのような矎しい安定したパタヌンが埗られたす。现胞は前で生き返り、埌ろで死に、グラむダヌはフィヌルドを暪切っお飛んでいるようです。



パルサヌ ゲヌムの「ラむフ」は非垞にシンプルです。フィヌルドずいく぀かのルヌルです。 むンタビュヌでこのタスクを䟝頌したす。これを行う方法がわからない堎合は、䜕も話すこずができたせん。 倚くの人はすぐに、「现胞は名詞です。 クラスが必芁です。「このクラスのプロパティは䜕ですか 堎所、生きおいるかどうか、次のタヌンの状態、すべお ただ隣人がいたす。 次に、フィヌルドの説明を開始したす。 フィヌルドは倚数のセルであるため、グリッドであり、内郚のセルをカりントする「カりント」メ゜ッドがありたす。



 class Cell(object): def __init__(self, x, y, alive=True): self.x = x self.y = y self.alive = alive self.next = None def neigbors(self): for i, j in itertools.product(range(-1, 2), repeat=2): if (i, j) != (0, 0): yield (self.x + i, self.y + j) class Board(object): def __init__(self): self.cells = {} # { (x, y): Cell() } def advance(self): for (x, y), cell in self.cells.items(): alive_neighbors = len(cell.neighbors) cell.next = (alive_neighbors == 3 or (alive_neighbors == 2 and cell.alive))
      
      







この時点で、「stop」ず蚀う必芁がありたす。Fieldクラスがあり、その䞭に__init__ず「make a move」の2぀のメ゜ッドがありたす。 その䞭に1぀のプロパティがありたす-蟞曞です。これは、蟞曞を操䜜する必芁があるこずを意味したす。 ポむントの近傍を保存する必芁はないこずに泚意しおください、それらは既に蟞曞にありたす。 生きおいるかどうかは単なるブヌル倀であるため、生きおいる现胞の座暙のみを保存したす。 たた、蟞曞にはTrueのみが栌玍されおいるため、蟞曞は必芁なく、単に座暙のセットが必芁です。 最埌に、新しい状態は必芁ありたせん。生现胞のリストを簡単に再䜜成できたす。



 def neigbors(point): x, y = point for i, j in itertools.product(range(-1, 2), repeat=2): if any((i, j)): yield (x + i, y + j) def advance(board): newstate = set() recalc = board | set(itertools.chain(*map(neighbors, board))) for point in recalc: count = sum((neigh in board) for neigh in neighbors(point)) if count == 3 or (count == 2 and point in board): newstate.add(point) return newstate glider = set([(0, 0), (1, 0), (2, 0), (0, 1), (1, 2)]) for i in range(1000): glider = advance(glider) print glider
      
      







グラむダヌの動き ゲヌムの非垞にシンプルで簡朔な実装が刀明したした。 ここでは2぀のクラスは必芁ありたせん。 例-グラむダヌの座暙、フィヌルドに挿入され、グラむダヌが飛行したす。 それだけです これは、ゲヌム「ラむフ」の完党な実装です。



たずめ





1. __init__を含む2぀のメ゜ッドを持぀クラスが衚瀺される堎合、これはクラスではありたせん。

2.必芁でない堎合および䞍芁な堎合、新しい䟋倖を䜜成しないでください。

3.簡玠化したす。



翻蚳者からコメントでは、倚くの人がこのレポヌトをPLOの完党な吊定だず認識しおいるこずがわかりたした。 これは間違いです。 結果のポむント1は、これがクラスではないこずを明確に瀺しおいたす。 クラスが必芁ですが、レポヌトの本質は、クラスが乱甚されるべきではないずいうこずです。



All Articles