web-dev-qa-db-ja.com

メソッドと関数の違い、Python C ++と比較)

私はPythonに関するCode Academyのチュートリアルを行っていますが、メソッドと関数の定義について少し混乱しています。チュートリアルから:

.upper().lower()str()などの文字列で使用した(または作成した)組み込み関数のいくつかについては既に知っています。 len()

C++からは、.upper().lower()はメソッドと呼ばれ、len()str()関数と呼ばれると思います。チュートリアルでは、これらの用語は同じ意味で使用されているようです。

PythonはC++のようにメソッドと関数を区別しますか?

メソッドと関数の違い とは異なり、Pythonの詳細について尋ねています。 「方法」と「機能」という用語は、リンクされた質問の受け入れられた答えで与えられた定義に常に従うとは限りません。

49
Q-bertsuit

functionはPythonの呼び出し可能オブジェクトです。つまり、call operatorを使用して呼び出すことができます(ただし、他のオブジェクトは___call___を実装することで関数をエミュレートできます)。例えば:

_>>> def a(): pass
>>> a
<function a at 0x107063aa0>
>>> type(a)
<type 'function'>
_

methodは特別なクラスの関数であり、boundまたはnboundになります。

_>>> class A:
...   def a(self): pass
>>> A.a
<unbound method A.a>
>>> type(A.a)
<type 'instancemethod'>

>>> A().a
<bound method A.a of <__main__.A instance at 0x107070d88>>
>>> type(A().a)
<type 'instancemethod'>
_

もちろん、バインドされていないメソッドを呼び出すことはできません(少なくとも引数としてインスタンスを渡さずに直接呼び出すことはできません)。

_>>> A.a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method a() must be called with A instance as first argument (got nothing instead)
_

Pythonでは、ほとんどの場合、バインドされたメソッド、関数、呼び出し可能なオブジェクト(___call___を実装するオブジェクト)、またはクラスコンストラクターの違いに気付かないでしょう。それらはすべて同じように見えますが、命名規則が異なります。ボンネットの下では、オブジェクトは大きく異なって見えるかもしれません。

これは、バインドされたメソッドを関数として使用できることを意味します。これは、Pythonを非常に強力にする小さなものの1つです。

_>>> b = A().a
>>> b()
_

また、len(...)str(...)(後者は型コンストラクター)の間に根本的な違いはありますが、少し深く掘り下げるまで違いに気付かないことも意味します。

_>>> len
<built-in function len>
>>> str
<type 'str'>
_
49
Krumelur

それでもメソッドがどのように機能するか理解していない場合は、実装を見て問題を明確にすることができます。データ属性ではないインスタンス属性が参照されると、そのクラスが検索されます。名前が関数オブジェクトである有効なクラス属性を示す場合、メソッドオブジェクトは、抽象オブジェクトで一緒に見つかったインスタンスオブジェクトと関数オブジェクトをパック(ポインター)することによって作成されます。これはメソッドオブジェクトです。メソッドオブジェクトが引数リストで呼び出されると、インスタンスオブジェクトと引数リストから新しい引数リストが作成され、この新しい引数リストで関数オブジェクトが呼び出されます。

http://docs.python.org/2/tutorial/classes.html#method-objects

この抜粋を注意深くお読みください。

その意味は :

1)インスタンスは、その属性になるメソッドであるオブジェクトを実際には保持しません。
実際、インスタンスの___dict___には「メソッド」属性がまったくありません(___dict___はオブジェクトの名前空間です)

2)「メソッド」属性が呼び出されたときにインスタンスに「メソッド」があるように見えるという事実は、インスタンスの名前空間内のメソッドオブジェクトの存在ではなく、プロセスによるものです。

3)また、クラスの名前空間にメソッドオブジェクトは実際には存在しません。

しかし、そのような呼び出しが行われたときに実際のメソッドオブジェクトにつながる何かがあるはずなので、インスタンスとの違いがありますか?

表現を簡単にするために、クラスの「メソッド」属性と呼ばれるものは、実際にはクラスの名前空間の属性であるfunctionオブジェクトです。
つまり、ペア(関数の識別子、関数)はクラスの___dict___のメンバーであり、この属性によりメソッド呼び出しが実行されたときにメソッドオブジェクトを構築するインタプリタ。

4)繰り返しますが、「メソッド」属性が呼び出されたときにクラスに「メソッド」があるように見えるという事実は、クラスの名前空間内のメソッドオブジェクトの存在ではなく、プロセスによるものです。

[〜#〜] edit [〜#〜]それ以上は確信がありません。最後に見る

5)メソッドオブジェクト(「メソッド」オブジェクトではなく、実際のオブジェクトが実際にメソッドであることを意味します、抜粋に記載されているもの)はcreatedです呼び出しの時点では、以前は存在しませんでした。
これは一種のラッパーです。インスタンスオブジェクトと、メソッドの基になっている関数オブジェクトへのポインターをパックします。

したがって、メソッドは関数に基づいています。この関数は、実際にはクラスの名前空間(___dict___)に属するため、この「メソッド」を保持するクラスの実際の属性です。この関数は、<function ......>_ ___dict___が出力されます。
この関数は、エイリアス_im_func_または___func___を使用してメソッドオブジェクトから到達できます(以下のコードを参照)

これらの概念はあまり一般的に知られておらず、理解されていないと思います。しかし、次のコードは私が言ったことを証明しています。

_class A(object):
    def __init__(self,b=0):
        self.b = b
    print 'The __init__ object :\n',__init__

    def addu(self):
        self.b = self.b + 10
    print '\nThe addu object :\n',addu


print '\nThe A.__dict__  items :\n',
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in A.__dict__.items())
a1 = A(101)
a2 = A(2002)

print '\nThe a1.__dict__  items:'
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in a1.__dict__.items())

print '\nThe a2.__dict__  items:'
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in a2.__dict__.items())

print '\nA.addu.__func__ :',A.addu.__func__
print id(A.addu.__func__),'==',hex(id(A.addu.__func__))
print

print 'A.addu :\n  ',
print A.addu,'\n  ',id(A.addu),'==',hex(id(A.addu))

print 'a1.addu :\n  ',
print a1.addu,'\n  ',id(a1.addu),'==',hex(id(a1.addu))
print 'a2.addu :\n  ',
print a2.addu,'\n  ',id(a2.addu),'==',hex(id(a2.addu))

a2.addu()
print '\na2.b ==',a2.b

print '\nThe A.__dict__  items :\n',
print '\n'.join('  {0:{align}11}  :  {1}'.format(*it,align='^')
                for it in A.__dict__.items())
_

結果

_The __init__ object :
<function __init__ at 0x011E54B0>

The addu object :
<function addu at 0x011E54F0>

The A.__dict__  items :
  __module__   :  __main__
     addu      :  <function addu at 0x011E54F0>
   __dict__    :  <attribute '__dict__' of 'A' objects>
  __weakref__  :  <attribute '__weakref__' of 'A' objects>
    __doc__    :  None
   __init__    :  <function __init__ at 0x011E54B0>

The a1.__dict__  items:
       b       :  101

The a2.__dict__  items:
       b       :  2002

A.addu.__func__ : <function addu at 0x011E54F0>
18765040 == 0x11e54f0

A.addu :
   <unbound method A.addu> 
   18668040 == 0x11cda08
a1.addu :
   <bound method A.addu of <__main__.A object at 0x00CAA850>> 
   18668040 == 0x11cda08
a2.addu :
   <bound method A.addu of <__main__.A object at 0x011E2B90>> 
   18668040 == 0x11cda08

a2.b == 2012

The A.__dict__  items :
  __module__   :  __main__
     addu      :  <function addu at 0x011E54F0>
   __dict__    :  <attribute '__dict__' of 'A' objects>
  __weakref__  :  <attribute '__weakref__' of 'A' objects>
    __doc__    :  None
   __init__    :  <function __init__ at 0x011E54B0>
_

編集

何かが私を困らせており、私は主題の深い内臓を知りません:

上記のコードは、_A.addu_、_a1.addu_および_a2.addu_がすべて同じメソッドオブジェクトであり、一意のIDを持つことを示しています。
ただし、_A.addu_は特定のインスタンスに関する情報がないため、非バインドメソッドと呼ばれます。
と_a1.addu_と_a2.addu_は、それぞれがメソッドの操作に関係する必要があるインスタンスを指定する情報を持っているため、バインドメソッドと呼ばれます。
論理的には、私にとっては、これら3つのケースごとに方法が異なるはずです。

[〜#〜] but [〜#〜]IDは3つすべてで同じであり、さらにこのIDは関数のIDとは異なりますメソッドのベース。
メソッドは実際にメモリ内に存在するオブジェクトであり、インスタンスからの呼び出しごとに別のインスタンスから別のcalに変更されないという結論に至ります。

[〜#〜] however [〜#〜]、クラスの名前空間___dict___を出力します。インスタンスと「メソッド」addu()の呼び出し、この名前空間は、addu関数とは異なるメソッドオブジェクトに識別される可能性のある新しいオブジェクトを公開しません。

どういう意味ですか ?
メソッドオブジェクトが作成されるとすぐに、オブジェクトオブジェクトは破棄されず、メモリ(RAM)に保存されるという印象を与えます。
しかし、それは隠されて生きており、インターピーターの機能を形成するプロセスのみが、それを見つける方法と場所を知っています。
この非表示オブジェクトである実際のメソッドオブジェクトには、関数を適用する必要があるインスタンスへの参照を変更する機能、または非バインドメソッドとして呼び出される場合はNoneを参照する機能が必要です。それは私には思えますが、それはブレインストーミングの仮説に過ぎません。

誰もこの尋問で何かを知っていますか?


質問に答えるには、_.upper_および_.lower_functionsを呼び出すのが正しいと考えることができます。クラス。

ただし、次の結果は特別なものです。おそらく、これらは組み込みのメソッド/関数であり、私のコードのようなユーザーのメソッド/関数ではありません。

_x = 'hello'
print x.upper.__func__
_

結果

_    print x.upper.__func__
AttributeError: 'builtin_function_or_method' object has no attribute '__func__'
_
4
eyquem

基本的に、はい、Pythonはそれらを区別しますが、Pythonでは、メソッドを関数のサブセットとして表示するのが一般的です。メソッドはクラスまたはインスタンスに関連付けられます。メソッドであるものは関数でもありますが、メソッドではない関数が存在する可能性があります。

Jon Clementsが彼のコメントで言及したように、しかし、その区別はC++ほど厳しくありません。スタンドアロン関数は、実行時にメソッドに「変換」できます。また、メソッドは、スタンドアロン関数と同じように効果的に動作するように変数に割り当てることができます。したがって、メソッドと関数の境界は透過的です。

2
BrenBarn

次のクラス定義では:

_class MyClass:
    """A simple example class"""
    def f(self):
        return 'hello world'
_
  • クラスMyClass
  • 関数f()
  • 方法なし(実際、該当なし)

上記のクラスのインスタンスを作成しましょう。これを行うには、class object, i.e. MyClass()を_var x_に割り当てます。

_  x = MyClass()
_

ここに、

  • 関数なし
  • メソッドx.f()

そして、xをMyClass()に割り当てたときに_function object MyClass.f_が(内部で)_method object x.f_を定義するために使用されたことを忘れないでください。

2
Ketan