Pythonコードが長さメソッドではなくlen()関数を使用するのはなぜですか?


204

Pythonにはlen()文字列のサイズを決定するために使用される関数があることは知っていますが、なぜそれが文字列オブジェクトのメソッドではないのか疑問に思いました。

更新

わかりました、私は恥ずかしいほど間違っていることに気付きました。__len__()実際には文字列オブジェクトのメソッドです。文字列オブジェクトでlen関数を使用してPythonでオブジェクト指向のコードを見るのは奇妙に思えます。さらに、__len__lenだけではなく名前として見るのもおかしいです。

回答:


180

文字列にはlengthメソッドがあります。 __len__()

Pythonのプロトコルは、長さを持ち、組み込みlen()関数を使用するオブジェクトにこのメソッドを実装することです。組み込み関数は、組み込み関数を実装__iter__()して使用iter()する方法(またはメソッドを背後で呼び出す方法)に似ています。反復可能なオブジェクトでのシーン)。

詳細については、コンテナタイプのエミュレートを参照してください。

Pythonのプロトコルについての良い読み物は次のとおりです。Pythonと最小驚きの原則


124
それを使う理由がどれほどおもしろいのか、私には驚きですlen。彼らは人々に強制する.__len__よりも人々に強制するほうが簡単だと考えています.len()。その同じこと、そして1つははるかにきれいに見えます。言語にOOPが含まれる場合、__len__世界で何を作成するかlen(..)
代替案

38
len、strなどは、メソッドを呼び出すだけの関数やラムダを定義する必要なく、map、reduce、filterなどの高次関数で使用できます。Pythonであっても、すべてがOOPを中心に展開しているわけではありません。
Evicatos 2013年

11
また、プロトコルを使用することにより、物事を実装する別の方法を提供できます。たとえば、で__iter__、またはのみ__getitem__でイテラブルを作成でき、iter(x)どちらの方法でも機能します。__bool__または__len__でブールコンテキストで使用可能なオブジェクトを作成でき、bool(x)どちらの方法でも機能します。等々。Arminはリンクされた投稿でこれについてかなりうまく説明していると思いますが、たとえ彼が説明しなかったとしても、コア
開発者

8
@Evicatos:「OOPの周りにいないすべての公転」のための+1が、私は「で終わるだろう、特にない、Pythonで」さえ。Python(Java、Ruby、Smalltalkとは異なります)は、「純粋なOOP」言語になることを試みません。「マルチパラダイム」言語になるように明示的に設計されています。リスト内包表記は、イテラブルのメソッドではありません。メソッドzipではなく、トップレベルの関数zip_withです。等々。すべてがオブジェクトであるからといって、オブジェクトであることはそれぞれについて最も重要なことではありません。
abarnert 2014年

7
その記事はあまりにも貧弱に書かれているので、読んでみたところ混乱して怒っています。「Python 2.xでは、たとえばタプル型は特別なメソッドを公開していませんが、それを使用して文字列を作成することができます:」全体は、ほとんど解読できない文章の融合です。
MirroredFate 2016

93

この質問に対するジムの回答が役立つかもしれません。ここにコピーします。グイドファンロッサムの引用:

まず、HCIの理由から、私はx.len()ではなくlen(x)を選択しました(def __len __()はかなり後に登場しました)。実際には2つの理由があり、どちらもHCIです。

(a)一部の操作では、前置記法は単にpostfixよりも読みやすくなります。前置(およびinfix!)操作は数学において長い伝統を有しており、ビジュアルが問題について数学者が考える助けとなる記法を好みます。x *(a + b)のような式をx a + x bに書き換える簡単な方法と、生のOO表記を使用して同じことを行う煩雑さを比較してください。

(b)len(x)と書かれたコードを読んだとき、何かの長さを要求していることがわかります。これは2つのことを教えてくれます。結果は整数であり、引数はなんらかのコンテナです。逆に、x.len()を読んだとき、xはインターフェイスを実装したり、標準のlen()を持つクラスから継承したりするコンテナの一種であることをすでに知っている必要があります。マッピングを実装していないクラスにget()またはkeys()メソッドがある場合、またはファイルではないクラスにwrite()メソッドがある場合にときどき発生する混乱を見てください。

同じことを別の言い方をすると、「len」は組み込みの操作と見なされます。それを失うのは嫌だ。/…/


37

lenメソッドがあります:

>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>

34

Pythonは実用的なプログラミング言語であり、len()関数strであってlistdictなどのメソッドではない理由は実用的です。

len()組み込み関数ディール直接とビルトインタイプ:のCPythonの実装len()実際には、の値を返しob_size、フィールドPyVarObjectCの構造体を表し、任意の可変サイズ内蔵メモリ内のオブジェクト。これは、メソッドを呼び出すよりもはるかに高速です。属性の検索を行う必要はありません。コレクション内の項目数を取得する一般的な操作であり、このような基本的かつ多様なタイプのために効率的に機能しなければならないstrlistarray.arrayなど

ただし、一貫性を高めるためlen(o)に、ユーザー定義型に適用する場合、Pythonはo.__len__()フォールバックとして呼び出します。 __len____abs__およびPythonデータモデルに記載されているその他すべての特別なメソッドにより、組み込みのように動作するオブジェクトを簡単に作成でき、「Pythonic」と呼ばれる表現力豊かで一貫性の高いAPIが可能になります。

特別なメソッドを実装することにより、オブジェクトは反復をサポートし、インフィックス演算子をオーバーロードし、withブロック内のコンテキストを管理できます。データモデルは、Python言語自体を、作成したオブジェクトをシームレスに統合できるフレームワークとして使用する方法と考えることができます。

このようなGuido van Rossumからの引用に裏付けられた2番目の理由は、にlen(s)比べて読み書きが簡単であることですs.len()

表記len(s)は、のような接頭表記の単項演算子と一致していますabs(n)。はよりlen()も頻繁に使用されておりabs()、書くのと同じくらい簡単であるに値します。

歴史的な理由もあるかもしれません:Pythonに先行する(そしてその設計に非常に影響力があった)ABC言語では、#sを意味するように書かれた単項演算子がありましたlen(s)


12
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.

34
もちろん、メソッドであるオブジェクトを操作する場合の明白なこと。
Peter Cooper

4
@ピーター:私は彼らがあなたのコメントをグイドの背中にテープで留めたという写真の証拠があれば誰にでも$ 20を払います。それが彼の額にあるなら50ドル。
バグ

1
そう、Pythonデザイナーはドグマを守っていますが、彼ら自身は自分のドグマを尊重していません。
bitek 2013

2
$ python -c 'これをインポートする' | 明らかなgrep
Ed Randall

4

ここにはいくつかの素晴らしい答えがあります。そのため、自分で説明する前に、ここで読んだいくつかの宝石(ルビーのしゃれは意図されていません)を強調したいと思います。

  • Pythonは純粋なOOP言語ではありません。これは、プログラマが最も使いやすいパラダイムやソリューションに最適なパラダイムをプログラマが使用できるようにする汎用のマルチパラダイム言語です。
  • Pythonにはファーストクラスの関数があるためlen、実際にはオブジェクトです。一方、Rubyにはファーストクラスの関数がありません。したがって、len関数オブジェクトには、を実行して検査できる独自のメソッドがありますdir(len)

独自のコードでこれが機能する方法が気に入らない場合は、好みの方法を使用してコンテナーを再実装するのは簡単です(以下の例を参照)。

>>> class List(list):
...     def len(self):
...         return len(self)
...
>>> class Dict(dict):
...     def len(self):
...         return len(self)
...
>>> class Tuple(tuple):
...     def len(self):
...         return len(self)
...
>>> class Set(set):
...     def len(self):
...         return len(self)
...
>>> my_list = List([1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'])
>>> my_dict = Dict({'key': 'value', 'site': 'stackoverflow'})
>>> my_set = Set({1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'})
>>> my_tuple = Tuple((1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'))
>>> my_containers = Tuple((my_list, my_dict, my_set, my_tuple))
>>>
>>> for container in my_containers:
...     print container.len()
...
15
2
15
15

0

あなたも言うことができます

>> x = 'test'
>> len(x)
4

Python 2.7.3を使用します。


9
len関数はすでに以前の回答で言及されていました。では、この「答え」の意味は何でしょうか。
Piotr Dobrogost 2014

0

ここでの残りの回答から欠落しているもの:len関数は、__len__メソッドが負でないを返すことを確認しますint。実際lenのクラスがチェックを回避するために、この動作をオーバーライドすることはできません機能手段です。このように、len(obj)安全性のレベルを提供しますobj.len()できない。

例:

>>> class A:
...     def __len__(self):
...         return 'foo'
...
>>> len(A())
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    len(A())
TypeError: 'str' object cannot be interpreted as an integer
>>> class B:
...     def __len__(self):
...         return -1
... 
>>> len(B())
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    len(B())
ValueError: __len__() should return >= 0

もちろん、lenグローバル変数として再割り当てすることで関数を「オーバーライド」することは可能ですが、これを行うコードは、クラスのメソッドをオーバーライドするコードよりも明らかに疑わしいものです。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.