encapsulation 静的メンバ - メンバー以外のメンバーとPythonのメンバー関数





変数 関数内 (5)


scaleはベクトルのメンバー単位の乗算に依存するので、私はメソッドとして乗算を実装し、より一般的scaleを定義することを検討します:

class Vector(object):
    def __init__(self, dX, dY):
        self._dX = dX
        self._dY = dY

    def __str__(self):
        return "->(" + str(self._dX) + ", " + str(self._dY) + ")"

    def __imul__(self, other):
        if other is Vector:
            self._dX *= other._dX
            self._dY *= other._dY
        else:
            self._dX *= other
            self._dY *= other

        return self

def scale(vector, scalar):
    vector *= scalar

したがって、カプセル化が維持されている間、クラスインタフェースは豊富で合理化されている。

私はPythonには比較的新しく、C ++やJavaのバックグラウンドから習得した習慣と言語の機能を調和させるのに苦労しています。

私が直面している最新の問題は、特にMeyerの「 Effective C ++ 」のItem 23で最もよくまとめられたカプセル化に関するものです

メンバー以外の非友人機能をメンバー機能に優先する

friendメカニズムがないのを無視して、 メンバ関数はPythonのメンバ関数よりも好ましいと考えられますか?

義務的なasinineの例:

class Vector(object):
    def __init__(self, dX, dY):
        self.dX = dX
        self.dY = dY

    def __str__(self):
        return "->(" + str(self.dX) + ", " + str(self.dY) + ")"

    def scale(self, scalar):
        self.dX *= scalar
        self.dY *= scalar

def scale(vector, scalar):
    vector.dX *= scalar
    vector.dY *= scalar

v = Vector(10, 20) scale(v, 2)を指定すると、ベクトルの大きさを2倍にするためにv.scale(2)またはscale(v, 2)を呼び出すことができます。

この場合にプロパティを使用しているという事実を考慮すると、2つのオプションのどちらがあれば、どちらが良いの?




メンバー以外の非友人機能をメンバー機能に優先する

これは設計思想であり、すべてのOOPパラダイムプログラミング言語に拡張することができます。 これの本質を理解すれば、その概念ははっきりしています

クラスのメンバーへのプライベート/保護されたアクセスを必要とせずに行うことができる場合、デザインには、クラスのメンバーである関数を含める理由がありません。 これを他の方法で考えるには、クラスを設計するときに、すべてのプロパティを列挙した後、クラスを作成するのに十分な最小限のビヘイビアセットを決定する必要があります。 使用可能なパブリックメソッド/メンバー関数のいずれかを使用して記述できるメンバー関数はすべて公開する必要があります。

これはPythonでどのくらい適用できますか

あなたが慎重であればある程度まで。 Pythonは他のOOP言語(Java / C ++など)と比較して弱いカプセル化をサポートしています。特にプライベートメンバーがないためです。 (変数名の前に '_'を接頭辞として付けることで、プログラマが簡単に書くことができるPrivate変数と呼ばれるものがあります。これは、名前のマングリング機能によってプライベートクラスになります)。 だから、スコット・マイヤーの言葉を文字通り採用すれば、クラスからアクセスすべきものと外部からのものとの間のような薄いものがあると考えている。 関数がClassまたはNotの不可欠な部分であるかどうかを決めるには、デザイナー/プログラマーに任せておくのが最善です。 "Unless your function required to access any of the properties of the class you can make it a non-member function".




興味深い質問。

あなたは、Javaプログラマーからのほとんどの質問とは別の場所から始めています。これは、ほとんどの場合はクラスが必要であると想定する傾向があります。 一般に、Pythonでは、特にデータのカプセル化をしていない限り、クラスを持つことに意味がありません。

もちろん、あなたの例では実際にそれをやっているので、クラスの使用は正当です。 個人的には、クラスを持っているのでメンバー関数が最善の方法だと言いたいと思います。具体的にはその特定のベクトルインスタンスを操作しているので、その関数がベクター。

あなたがそれを必ずしも互いに継承する必要のない複数のクラスで動作させる必要がある場合は、スタンドアロンの関数にしたいかもしれません( "member"または "non-member"という言葉は実際に使用しません)。共通の基盤。 ダックタイピングのおかげで、これを行うのはかなり一般的なことです:あなたの関数が特定の属性やメソッドのセットを持つオブジェクトを期待していることを指定し、それらを使って何かを行います。




独自の例を見てください。非メンバ関数は、Vectorクラスのデータメンバーにアクセスする必要があります。 これはカプセル化のための勝利ではありません。 これは、渡されたオブジェクトのデータメンバを変更するときに特にそうです。この場合は、スケーリングされたベクトルを返し、元のまま変更しない方がよい場合があります。

さらに、非メンバ関数を使ってクラス多形性のメリットを実感することはできません。 例えば、この場合、2つの成分のベクトルにしか対処できない。 ベクトル乗算機能を使用した方が良いか、またはコンポーネントを反復処理するメソッドを使用する方が良いでしょう。

要約すれば:

  1. メンバ関数を使用して、制御するクラスのオブジェクトを操作します。
  2. 非メンバ関数を使用して、それ自体が多相であるメソッドおよび演算子によって実装される純粋に汎用の演算を実行します。
  3. おそらく、メソッドにオブジェクトの変異を残すほうがよいでしょう。



オブジェクトとしてのクラス

メタクラスを理解する前に、Pythonでクラスをマスターする必要があります。 そして、Pythonには、Smalltalk言語から借りてきたクラスについての非常に特殊な考えがあります。

ほとんどの言語では、クラスはオブジェクトを生成する方法を記述する単なるコードの一部です。 それはPythonでもまあまあです。

>>> class ObjectCreator(object):
...       pass
...

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

しかし、クラスはPythonのそれ以上のものです。 クラスもオブジェクトです。

はい、オブジェクト。

キーワードclassを使用するとすぐに、Pythonはそれを実行してOBJECTを作成します。 命令

>>> class ObjectCreator(object):
...       pass
...

メモリに "ObjectCreator"という名前のオブジェクトを作成します。

このオブジェクト(クラス)はそれ自体がオブジェクト(インスタンス)を作成することができ、これがクラスです。

しかしそれでも、それは目的であり、したがって:

  • それを変数に割り当てることができます
  • あなたはそれをコピーすることができます
  • あなたはそれに属性を追加することができます
  • 関数のパラメータとして渡すことができます

例えば:

>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
...       print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>

動的にクラスを作成する

クラスはオブジェクトなので、他のオブジェクトと同様に、その場で作成することができます。

まず、クラスを使用して関数内にクラスを作成できます。

>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

しかし、ダイナミックではありません。クラス全体を自分で書く必要があるからです。

クラスはオブジェクトなので、何かによって生成されなければなりません。

classキーワードを使用すると、Pythonはこのオブジェクトを自動的に作成します。 しかし、Pythonのほとんどのものと同様、手動で行う方法もあります。

関数type覚えていますか? オブジェクトがどのような型であるかを知る良い古い関数です:

>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

まあ、 typeはまったく違った能力を持っていますし、その場でクラスを作成することもできます。 typeはクラスの記述をパラメータとして取り、クラスを返すことができます。

(私が知っているのは、渡したパラメータに応じて同じ関数が2つの全く異なる用途を持つことができないということは愚かです。これはPythonの下位互換性のために問題になります)

typeは次のように動作します:

type(name of the class,
     tuple of the parent class (for inheritance, can be empty),
     dictionary containing attributes names and values)

例えば:

>>> class MyShinyClass(object):
...       pass

この方法で手動で作成することができます:

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

クラスの名前として "MyShinyClass"を使用し、クラス参照を保持する変数として使用することに気づくでしょう。 彼らは違うかもしれませんが、事を複雑にする理由はありません。

typeは、クラスの属性を定義するための辞書を受け入れます。 そう:

>>> class Foo(object):
...       bar = True

翻訳できる:

>>> Foo = type('Foo', (), {'bar':True})

そして通常のクラスとして使用されます:

>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

もちろん、あなたはそれを継承することができます:

>>>   class FooChild(Foo):
...         pass

だろう:

>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

最終的に、クラスにメソッドを追加したいと思うでしょう。 適切な署名を持つ関数を定義し、それを属性として割り当てます。

>>> def echo_bar(self):
...       print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

また、通常作成されたクラスオブジェクトにメソッドを追加するのと同様に、クラスを動的に作成した後でさらにメソッドを追加できます。

>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

私たちはどこに行くのか見ていきます。Pythonでは、クラスはオブジェクトであり、動的にクラスを動的に作成することができます。

これは、キーワードclassを使用するときにPythonが実行する動作で、メタクラスを使用することによって実行されます。

メタクラスとは(最終的に)

メタクラスはクラスを作成する 'もの'です。

オブジェクトを作成するためにクラスを定義するのは正しいでしょうか?

しかし、私たちはPythonクラスがオブジェクトであることを学びました。

さて、これらのオブジェクトを作成するのはメタクラスです。 彼らはクラスのクラスです、あなたはそれらをこのように描くことができます:

MyClass = MetaClass()
my_object = MyClass()

あなたは、あなたが次のようなことをすることができることを見てきました:

MyClass = type('MyClass', (), {})

これは、関数typeが実際にはメタクラスであるためです。 typeは、Pythonがバックグラウンドですべてのクラスを作成するために使用するメタクラスです。

さて、なぜあなたはそれが小文字で書かれているのだろうと思っています。

文字列オブジェクトを作成するクラスstrと整数オブジェクトを作成するクラスintとの整合性の問題だと思います。 typeはクラスオブジェクトを作成するクラスです。

それは__class__属性をチェックすることで__class__ます。

すべて、そして私はすべてを意味し、Pythonのオブジェクトです。 これには、int、文字列、関数、クラスが含まれます。 それらはすべてオブジェクトです。 そしてそれらのすべてはクラスから作成されています:

>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

さて、どの__class__は何__class__

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

ですから、メタクラスはクラスオブジェクトを作成するだけのものです。

あなたが望むなら、それを「クラスファクトリー」と呼ぶことができます。

typeは組み込みのメタクラスPythonが使用するものですが、もちろん独自のメタクラスを作成することもできます。

__metaclass__属性

Python 2では、クラスを書くときに__metaclass__属性を追加することができます(次のPython 3の構文を参照)。

class Foo(object):
    __metaclass__ = something...
    [...]

そうすると、Pythonはメタクラスを使ってクラスFooを作成します。

細心の注意を払っています。

class Foo(object)最初に書きclass Foo(object)が、クラスオブジェクトFooはまだメモリに作成されていません。

Pythonはクラス定義で__metaclass__を探します。 見つかった場合は、それを使用してオブジェクトクラスFooを作成します。 そうでなければ、 typeを使ってクラスを作成します。

それを数回読んでください。

あなたがするとき:

class Foo(Bar):
    pass

Pythonは以下を行います:

Foo __metaclass__属性がありますか?

もしそうなら、 __metaclass__ものを使ってFooという名前でクラスオブジェクト(私はここでクラスオブジェクトと言っています)をメモリに作成します。

Pythonが__metaclass__見つけられない場合は、MODULEレベルで__metaclass__を探し、 __metaclass__を継承しないクラス(基本的に古いスタイルのクラスのみ)で同じことを試みます。

そして、__metaclass__がまったく見つからない場合は、クラスのオブジェクトを作成するためにBar (最初の親)のメタクラス(デフォルトのtypeかもしれません)を使用します。

__metaclass__属性が継承されないように注意してください。親( Bar.__class__ )のメタクラスが継承されます。 Bartype()type.__new__()type.__new__()ではなくBarを作成した__metaclass__属性を使用した場合、サブクラスはその動作を継承しません。

今大きな問題は、 __metaclass__何を入れることができるかということです。

答えは、クラスを作成できるものです。

クラスを作ることができるのは何ですか? type 、またはそれをサブクラス化または使用するもの。

Python 3のメタクラス

メタクラスを設定する構文はPython 3で変更されています:

class Foo(object, metaclass=something):
    [...]

つまり、 __metaclass__属性は使用されなくなり、基本クラスのリストのキーワード引数が優先されます。

しかし、メタクラスの動作はほとんど変わりません。

カスタムメタクラス

メタクラスの主な目的は、作成時にクラスを自動的に変更することです。

通常は、現在のコンテキストと一致するクラスを作成するAPIに対してこれを行います。

あなたのモジュール内のすべてのクラスが大文字で書かれた属性を持つべきだと決める愚かな例を想像してみてください。 これを行うにはいくつかの方法がありますが、モジュールレベルで__metaclass__を設定する方法もあります。

このようにして、このモジュールのすべてのクラスはこのメタクラスを使用して作成され、すべての属性を大文字にするようにメタクラスに指示するだけで済みます。

幸いなことに、 __metaclass__は実際には__metaclass__ことができますが、正式なクラスである必要はありません(私の知っているように、クラスの名前はクラスである必要はありません。

だから、関数を使って簡単な例から始めましょう。

# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
    """
      Return a class object, with the list of its attribute turned
      into uppercase.
    """

    # pick up any attribute that doesn't start with '__' and uppercase it
    uppercase_attr = {}
    for name, val in future_class_attr.items():
        if not name.startswith('__'):
            uppercase_attr[name.upper()] = val
        else:
            uppercase_attr[name] = val

    # let `type` do the class creation
    return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr # this will affect all classes in the module

class Foo(): # global __metaclass__ won't work with "object" though
    # but we can define __metaclass__ here instead to affect only this class
    # and this will work with "object" children
    bar = 'bip'

print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True

f = Foo()
print(f.BAR)
# Out: 'bip'

さて、まったく同じようにしましょうが、実際のクラスをメタクラスに使用しましょう:

# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
    # __new__ is the method called before __init__
    # it's the method that creates the object and returns it
    # while __init__ just initializes the object passed as parameter
    # you rarely use __new__, except when you want to control how the object
    # is created.
    # here the created object is the class, and we want to customize it
    # so we override __new__
    # you can do some stuff in __init__ too if you wish
    # some advanced use involves overriding __call__ as well, but we won't
    # see this
    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type(future_class_name, future_class_parents, uppercase_attr)

しかし、これは本当にOOPではありません。 type直接呼びますが、親をオーバーライドしたり__new__呼び出すことはありません。 やってみましょう:

class UpperAttrMetaclass(type):

    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)

あなたは余分な引数upperattr_metaclass気づいたかもしれません。 特別なことは何もありません__new__は、最初に定義されたクラスを常に最初のパラメータとして受け取ります。 インスタンスを最初のパラメータとして受け取る通常のメソッド、またはクラスメソッドの定義クラスのためのselfを持っているように。

もちろん、私がここで使った名前は分かりやすくするために長いですが、 selfように、すべての議論は従来の名前を持っています。 したがって、実際の制作メタクラスは次のようになります。

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type.__new__(cls, clsname, bases, uppercase_attr)

継承を簡単にするsuperを使用することで、より洗練されたものにすることができます(メタクラスを継承し、メタクラスを継承し、継承します)。

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

それでおしまい。 実際にはメタクラスについては何もありません。

メタクラスを使用するコードの複雑さの背後にある理由は、メタクラスのためではありません。なぜなら、通常、メタクラスを使用してイントロスペクション、継承の操作、__dict__などの変数などに依存するねじれた処理を行うからです。

確かに、メタクラスは黒い魔法を実行するのに特に役に立ち、したがって複雑なものです。 しかし、それ自体はシンプルです。

  • クラス作成を傍受する
  • クラスを変更する
  • 変更されたクラスを返す

なぜ関数の代わりにメタクラスクラスを使うのですか?

__metaclass__は呼び出し可能なものを受け入れることができるので、明らかにもっと複雑なので、なぜクラスを使うのですか?

これにはいくつかの理由があります。

  • その意図ははっきりしています。 あなたがUpperAttrMetaclass(type)を読むとき、あなたは何が続くのか知っています
  • あなたはOOPを使うことができます。 メタクラスはメタクラスを継承し、親メソッドをオーバーライドすることができます。 メタクラスはメタクラスを使用することもできます。
  • クラスのサブクラスは、メタクラスクラスを指定した場合はそのメタクラスのインスタンスになりますが、メタクラス関数ではない場合は、そのメタクラスのインスタンスになります。
  • コードをよりよく構造化することができます。 上記の例のように、何かのことでメタクラスを使用することは決してありません。 これは通常、何か複雑なものです。 複数のメソッドを作成して1つのクラスにグループ化することは、コードを読みやすくするために非常に便利です。
  • __new____new____new__にフックでき__call__ 。 これは、あなたが別のものを行うことができます。 通常は__new__ですべてを行うことができますが、 __new__を使うほうが快適です。
  • これらはメタクラスと呼ばれています。 何かを意味する必要があります!

なぜメタクラスを使用しますか?

今大きな問題です。 なぜ、あいまいなエラーが発生しやすい機能を使用しますか?

まあ、あなたはしない:

メタクラスは、ユーザーの99%が心配するべきではない深い魔法です。 あなたがそれらを必要としているかどうか疑問に思うなら、あなたは(実際に必要とする人々は、彼らが必要とすることを確実に知り、理由について説明する必要はありません)。

Python Guru Tim Peters

メタクラスの主な使用例は、APIを作成することです。 これの典型的な例は、Django ORMです。

このように定義することができます:

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

しかし、あなたがこれを行う場合:

guy = Person(name='bob', age='35')
print(guy.age)

IntegerFieldオブジェクトは返されません。 intを返し、データベースから直接取得することもできます。

これは可能ですmodels.Model__metaclass__を定義しており、単純な文で定義したPersonをデータベースフィールドの複雑なフックに変換する魔法を使用しています。

Djangoは、シンプルなAPIを公開し、メタクラスを使用してこのAPIのコードを再作成することで複雑な外観をシンプルにしています。

最後の言葉

まず、クラスはインスタンスを作成できるオブジェクトであることがわかります。

実際、クラス自体はインスタンスです。 メタクラスの

>>> class Foo(object): pass
>>> id(Foo)
142630324

すべてがPythonのオブジェクトであり、それらはすべてクラスのインスタンスまたはメタクラスのインスタンスのいずれかです。

type除く。

typeは実際には独自のメタクラスです。 これは、純粋なPythonで再現できるものではなく、実装レベルでちょっとした欺瞞をしています。

第二に、メタクラスは複雑です。 非常に簡単なクラス変更には使用したくないかもしれません。 次の2つの方法を使用してクラスを変更できます。

クラスの変更が必要な時間の99%は、これらを使用する方がよいでしょう。

しかし、98%の時間で、クラスの変更はまったく必要ありません。





python encapsulation member-functions non-member-functions