サーチ…


前書き

Pythonは一般的なスクリプティング言語としてだけでなく、オブジェクト指向プログラミングパラダイムもサポートしています。クラスはデータを記述し、そのデータを操作するメソッドを提供します。これらのメソッドはすべて単一のオブジェクトの下にあります。さらに、クラスでは、具体的な実装の詳細をデータの抽象的な表現から分離することによって、抽象化が可能です。

クラスを利用するコードは、一般的に読みやすく、理解し、維持するのがより簡単です。

基本的な継承

Pythonの継承は、Java、C ++などの他のオブジェクト指向言語で使用されている同様の考え方に基づいています。新しいクラスは、既存のクラスから次のように派生することができます。

class BaseClass(object):
    pass

class DerivedClass(BaseClass):
    pass

BaseClassはすでに存在する( )クラスで、 DerivedClassBaseClassから属性を継承する(またはサブクラス化する)新しい( )クラスです。 注意 :Python 2.2以降、すべてのクラスはすべての組み込み型の基本クラスであるobjectクラスを暗黙的に継承していobject

以下の例では親Rectangleクラスを定義しています。これは暗黙的にobjectから継承していobject

class Rectangle():
    def __init__(self, w, h):
        self.w = w
        self.h = h
    
    def area(self):
        return self.w * self.h
        
    def perimeter(self):
        return 2 * (self.w + self.h)

Rectangleクラスは、 Squareクラスを定義するための基本クラスとして使用できます。正方形は長方形の特殊なケースです。

class Square(Rectangle):
    def __init__(self, s):
        # call parent constructor, w and h are both s
        super(Square, self).__init__(s, s)
        self.s = s

Squareクラスは自動的にRectangleクラスとオブジェクトクラスのすべての属性を継承します。 super()Rectangleクラスの__init__()メソッドを呼び出すために使用され、基本クラスのオーバーライドされたメソッドを本質的に呼び出します。 注意 :Python 3では、 super()引数は必要ありません。

派生クラスオブジェクトは、基本クラスの属性にアクセスして変更できます。

r.area()
# Output: 12
r.perimeter()  
# Output: 14

s.area()
# Output: 4
s.perimeter()
# Output: 8

継承を使用する組み込み関数

issubclass(DerivedClass, BaseClass) :返すTrue場合DerivedClassのサブクラスであるBaseClass

isinstance(s, Class) :返すTrue sはのインスタンスである場合はClassまたは派生クラスの任意のClass

# subclass check        
issubclass(Square, Rectangle)
# Output: True

# instantiate
r = Rectangle(3, 4)
s = Square(2)

isinstance(r, Rectangle)  
# Output: True
isinstance(r, Square)
# Output: False
# A rectangle is not a square

isinstance(s, Rectangle)
# Output: True
# A square is a rectangle
isinstance(s, Square)
# Output: True

クラス変数とインスタンス変数

インスタンス変数はインスタンスごとに一意であり、クラス変数はすべてのインスタンスで共有されます。

class C:
    x = 2  # class variable

    def __init__(self, y):
        self.y = y  # instance variable

C.x
# 2
C.y
# AttributeError: type object 'C' has no attribute 'y'

c1 = C(3)
c1.x
# 2
c1.y
# 3

c2 = C(4)
c2.x
# 2
c2.y
# 4

クラス変数はこのクラスのインスタンスでアクセスできますが、class属性に割り当てると、クラス変数をシャドウするインスタンス変数が作成されます

c2.x = 4
c2.x
# 4
C.x
# 2

インスタンスからクラス変数を変異させると 、いくつかの予期せぬ結果を招くことに注意してください。

class D:
    x = []
    def __init__(self, item):
        self.x.append(item)  # note that this is not an assigment!

d1 = D(1)
d2 = D(2)

d1.x
# [1, 2]
d2.x
# [1, 2]
D.x
# [1, 2]

バインドされた、バインドされていない、および静的メソッド

バインドされたメソッドとバインドされていないメソッドのアイデアは、Python 3では削除されました 。 Python 3では、クラス内でメソッドを宣言するときに、 defキーワードを使用して関数オブジェクトを作成しています。これは通常の関数で、周囲のクラスはその名前空間として機能します。次の例では、メソッドfをクラスA内で宣言し、関数Afになります。

Python 3.x 3.0
class A(object):
    def f(self, x):
        return 2 * x
A.f
# <function A.f at ...>  (in Python 3.x)

Python 2では、動作が異なりました。クラス内の関数オブジェクトは、特定のクラスインスタンスにバインドされていないため、 アンバインドされたメソッドと呼ばれるinstancemethod型のオブジェクトに暗黙的に置き換えられました。 .__func__プロパティを使用して基礎となる関数にアクセスすることは可能.__func__

Python 2.x 2.3
A.f
# <unbound method A.f>   (in Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>

後者の動作は検査によって確認されます。メソッドはPython 3の関数として認識されますが、Python 2では区別されています。

Python 3.x 3.0
import inspect

inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
Python 2.x 2.3
import inspect

inspect.isfunction(A.f)
# False
inspect.ismethod(A.f)
# True

両方のバージョンのPython関数/メソッドでは、クラスAインスタンスを最初の引数として渡すと、 Afを直接呼び出すことができます。

A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
#                      A instance as first argument (got int instance instead) 
# Python 3: 14   
a = A()
A.f(a, 20)
# Python 2 & 3: 40

今と仮定したクラスのインスタンスであるa Aが何であるか、 af 、その後?まあ、直感的にこれは同じ方法でなければなりませんfクラスのAのみ、それは何らかの形でそれがオブジェクトに適用されたことを「知る」必要がある、と結合した Pythonでこれが呼び出されるメソッド- 。 a a

次のように核心ザラザラの詳細は以下のとおりです。書き込みaf魔法呼び出す__getattribute__方法かどうかを最初にチェック、という名前の属性がありa a f 、クラスのチェック(そうでない)、 A 、それは、そのような名前を持つメソッドが含まれているかどうかを(それはありません)、および新しいオブジェクト作成m型のmethod原を参照有するAfにおけるm.__func__ 、およびオブジェクトへの参照aにおけるm.__self__ 。このオブジェクトが関数として呼び出されると、 m(...) => m.__func__(m.__self__, ...)ます。このため、このオブジェクトはバインドされたメソッドと呼ばれます。これは、呼び出されると、 バインドされたオブジェクトに最初の引数として指定することができるためです。 (これらはPython 2と3で同じように動作します)。

a = A()
a.f
# <bound method A.f of <__main__.A object at ...>>
a.f(2)
# 4

# Note: the bound method object a.f is recreated *every time* you call it:
a.f is a.f  # False
# As a performance optimization you can store the bound method in the object's
# __dict__, in which case the method object will remain fixed:
a.f = a.f
a.f is a.f  # True

最後に、Pythonにはクラスメソッド静的メソッドつまり特別な種類のメソッドがあります。クラスメソッドは、通常のメソッドと同じように動作します。ただし、オブジェクトに対して呼び出されると、オブジェクトではなくオブジェクトのクラスにバインドされます。したがって、 m.__self__ = type(a) 。このようなバインドされたメソッドを呼び出すと、 aのクラスが最初の引数として渡されます。静的メソッドはさらに簡単です。何もバインドしませんし、変換なしで基本関数を単純に返します。

class D(object):
    multiplier = 2

    @classmethod
    def f(cls, x):
        return cls.multiplier * x

    @staticmethod
    def g(name):
        print("Hello, %s" % name)

D.f
# <bound method type.f of <class '__main__.D'>>
D.f(12)
# 24
D.g
# <function D.g at ...>
D.g("world")
# Hello, world

クラスメソッドは、インスタンス上でアクセスされた場合でもクラスにバインドされていることに注意してください。

d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2, 1337)
d.f
# <bound method D.f of <class '__main__.D'>>
d.f(10)
# 20

最低レベルでは、関数、メソッド、 __get__メソッドなどは、実際には__set __、__ __set __、オプションで__del__特殊メソッドを呼び出す記述子であることに__get__して__set 。 classmethodsとstaticmethodsの詳細については:

新しいスタイルと古いスタイルのクラス

Python 2.x 2.2.0

新しいスタイルのクラスがPython 2.2で導入され、 クラスを統合しました 。それらはトップレベルのobject型から継承しobject新しいスタイルのクラスはユーザー定義型で 、組み込みと非常によく似ています。

# new-style class
class New(object):
    pass

# new-style instance
new = New()

new.__class__
# <class '__main__.New'>
type(new)
# <class '__main__.New'>
issubclass(New, object)
# True

古いスタイルのクラスはobjectから継承しませ 。旧式のインスタンスは、常に組み込みのinstance型で実装されinstance

# old-style class
class Old:
    pass

# old-style instance
old = Old()

old.__class__
# <class __main__.Old at ...>
type(old)
# <type 'instance'>
issubclass(Old, object)
# False
Python 3.x 3.0.0

Python 3では、古いスタイルのクラスが削除されました。

Python 3の新しいスタイルのクラスは暗黙的にobjectから継承されるので、 MyClass(object)もう指定する必要はありません。

class MyClass:
    pass

my_inst = MyClass()

type(my_inst)
# <class '__main__.MyClass'>
my_inst.__class__
# <class '__main__.MyClass'>
issubclass(MyClass, object)
# True

インスタンス変数のデフォルト値

変数に不変型(例えば文字列)の値が含まれている場合は、このようにデフォルト値を代入することができます

class Rectangle(object):
    def __init__(self, width, height, color='blue'):
        self.width = width
        self.height = height
        self.color = color
    
    def area(self):
        return self.width  * self.height 

# Create some instances of the class
default_rectangle = Rectangle(2, 3)
print(default_rectangle.color) # blue

red_rectangle = Rectangle(2, 3, 'red')
print(red_rectangle.color) # red

コンストラクタ内のリストなどの可変オブジェクトを初期化するときは注意が必要です。次の例を考えてみましょう。

class Rectangle2D(object):
    def __init__(self, width, height, pos=[0,0], color='blue'):  
        self.width = width
        self.height = height
        self.pos = pos
        self.color = color

r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [4, 0] r2's pos has changed as well

この動作は、Pythonのデフォルトのパラメータが関数の実行時にバインドされ、関数の宣言ではバインドされないという事実によって発生します。インスタンス間で共有されないデフォルトのインスタンス変数を取得するには、次のような構文を使用する必要があります。

class Rectangle2D(object):
    def __init__(self, width, height, pos=None, color='blue'):  
        self.width = width
        self.height = height
        self.pos = pos or [0, 0] # default value is [0, 0]
        self.color = color

r1 = Rectangle2D(5,3)
r2 = Rectangle2D(7,8)
r1.pos[0] = 4
r1.pos # [4, 0]
r2.pos # [0, 0] r2's pos hasn't changed 

Mutable Default Arguments「Least Astonishment」および 変更可能なデフォルト引数を参照してください。

多重継承

PythonはC3線形化アルゴリズムを使用して、メソッドを含むクラス属性を解決する順序を決定します。これは、Method Resolution Order(MRO)と呼ばれます。

ここに簡単な例があります:

class Foo(object):
    foo = 'attr foo of Foo'
    

class Bar(object):
    foo = 'attr foo of Bar' # we won't see this.
    bar = 'attr bar of Bar'

class FooBar(Foo, Bar):
    foobar = 'attr foobar of FooBar'

ここでFooBarをインスタンス化すると、foo属性を調べると、Fooの属性が最初に見つかることがわかります

fb = FooBar()

そして

>>> fb.foo
'attr foo of Foo'

FooBarのMROは次のとおりです。

>>> FooBar.mro()
[<class '__main__.FooBar'>, <class '__main__.Foo'>, <class '__main__.Bar'>, <type 'object'>]

PythonのMROアルゴリズムは

  1. 深さは最初に(例えばFooBarFoo
  2. 共有親( object )子(によってブロックされているBar )と
  3. 循環的な関係は許されません。

つまり、たとえば、BarはFooBarから継承できませんが、FooBarはBarから継承します。

Pythonの包括的な例については、 wikipediaのエントリーを参照してください。

継承のもう一つの強力な機能はsuperです。 superは親クラスのフィーチャーをフェッチできます。

class Foo(object):
    def foo_method(self):
        print "foo Method"

class Bar(object):
    def bar_method(self):
        print "bar Method"

class FooBar(Foo, Bar):
    def foo_method(self):
        super(FooBar, self).foo_method()

クラスのinitメソッドを使った多重継承。すべてのクラスに独自のinitメソッドがある場合、複数のineritanceを試してから、initメソッドが最初に継承するクラスを呼び出すだけです。

たとえば下の唯一のFooクラスのinitメソッドが呼び出さ取得バークラスが呼び出され得ていないinitの

    class Foo(object):
        def __init__(self):
            print "foo init"

    class Bar(object):
        def __init__(self):
            print "bar init"

    class FooBar(Foo, Bar):
        def __init__(self):
            print "foobar init"
            super(FooBar, self).__init__()

    a = FooBar()

出力:

    foobar init
    foo init

しかし、 Barクラスが継承されているわけではありません。最終的なFooBarクラスのインスタンスは、 BarクラスとFooクラスのインスタンスでもあります。

print isinstance(a,FooBar)
print isinstance(a,Foo)
print isinstance(a,Bar) 

出力:

True
True
True

記述子とドット付き検索

記述子は、(通常は)クラスの属性とそれは、任意の持っているオブジェクトである__get____set__ 、または__delete__特別な方法を。

データ記述 __set__には__set__または__delete__いずれかがあり__delete__

これらは、インスタンス上の点線ルックアップを制御することができ、関数、 staticmethodclassmethod 、およびpropertyを実装するために使用されproperty 。点線のルックアップ(例えば、 Fooクラスのインスタンスfooは、属性bar探していbar - つまりfoo.bar )は以下のアルゴリズムを使用します:

  1. barはクラスFooで検索されます。それはそこにあり、それがデータ記述子である場合には、データ記述子が使用されています。これはpropertyがインスタンス内のデータへのアクセスを制御する方法であり、インスタンスはこれをオーバーライドできません。 データ記述子が存在しない場合は、

  2. barはインスタンス__dict__で検索されます。このため、インスタンスから呼び出されるメソッドを点線のルックアップでオーバーライドまたはブロックすることができます。インスタンス内にbarが存在する場合は、そのインスタンスが使用されます。そうでなければ、我々は

  3. クラスFoobar探しbarDescriptorの場合、記述子プロトコルが使用されます。これは、関数(このコンテキストでは、バインドされていないメソッド)、 classmethod 、およびstaticmethodがどのように実装されるかです。それ以外の場合は、単にそのオブジェクトを返します。そうでない場合は、 AttributeError

クラスメソッド:代替初期化子

クラスメソッドは、クラスのインスタンスを構築する別の方法を示します。説明するために、例を見てみましょう。

比較的簡単なPersonクラスがあるとしましょう:

class Person(object):

    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.full_name = first_name + " " + last_name
    
    def greet(self):
        print("Hello, my name is " + self.full_name + ".")

このクラスのインスタンスを構築して、姓と名の別名を別々に指定する方法があれば便利かもしれません。これを行う1つの方法は、 last_nameにオプションのパラメータを指定することです。指定されていない場合は完全名を渡します:

class Person(object):

    def __init__(self, first_name, age, last_name=None):
        if last_name is None:
            self.first_name, self.last_name = first_name.split(" ", 2)
        else:
            self.first_name = first_name
            self.last_name = last_name
        
        self.full_name = self.first_name + " " + self.last_name
        self.age = age

    def greet(self):
        print("Hello, my name is " + self.full_name + ".")

しかし、このコードには2つの大きな問題があります。

  1. パラメータのfirst_namelast_nameあなたが完全な名前を入力することができるので、今紛らわしいですfirst_name 。また、この種の柔軟性を持つより多くのケースやパラメータがあれば、if / elif / elseブランチングは厄介なものになります。

  2. それほど重要ではありませんが、それでも指摘する価値があります:もしlast_nameNoneであれば、 first_nameはスペースで2つ以上のものに分割されませんか?入力の検証や例外処理の別の層があります...

クラスメソッドを入力します。単一のイニシャライザをfrom_full_nameするのではfrom_full_namefrom_full_nameという別のイニシャライザを作成し、(組み込みの) classmethodデコレータでデコレートします。

class Person(object):

    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self.age = age
        self.full_name = first_name + " " + last_name
    
    @classmethod
    def from_full_name(cls, name, age):
        if " " not in name:
            raise ValueError
        first_name, last_name = name.split(" ", 2)
        return cls(first_name, last_name, age)
    
    def greet(self):
        print("Hello, my name is " + self.full_name + ".")

from_full_name最初の引数としてself代わりにclsfrom_full_nameます。クラスメソッドは、与えられたクラスのインスタンスではなく 、全体のクラスに適用されます(これは、通常、 selfが意味するものです)。したがって、 clsPersonクラスである場合、 from_full_nameクラスメソッドから返される値はPerson(first_name, last_name, age)であり、 Personクラスのインスタンスを作成するためにPerson__init__を使用します。特に、 Employee of Personサブクラスを作成する場合、 from_full_nameEmployeeクラスでも動作します。

これが期待どおりに動作することを示すために、 __init__分岐しないで複数の方法でPersonインスタンスを作成しましょう:

In [2]: bob = Person("Bob", "Bobberson", 42)

In [3]: alice = Person.from_full_name("Alice Henderson", 31)

In [4]: bob.greet()
Hello, my name is Bob Bobberson.

In [5]: alice.greet()
Hello, my name is Alice Henderson.

その他の参考資料:

クラス構成

クラス構成は、オブジェクト間の明示的な関係を可能にする。この例では、人々は国に属する都市に住んでいます。構成によって、国に住むすべての人の数にアクセスできます。

class Country(object):
    def __init__(self):
        self.cities=[]
        
    def addCity(self,city):
        self.cities.append(city)
        

class City(object):
    def __init__(self, numPeople):
        self.people = []
        self.numPeople = numPeople
        
        
    def addPerson(self, person):
        self.people.append(person)
    
    def join_country(self,country):
        self.country = country
        country.addCity(self)
        
        for i in range(self.numPeople):
                person(i).join_city(self)
  

class Person(object):
    def __init__(self, ID):
        self.ID=ID

    def join_city(self, city):
        self.city = city
        city.addPerson(self)
        
    def people_in_my_country(self):
        x= sum([len(c.people) for c in self.city.country.cities])
        return x
        
US=Country()
NYC=City(10).join_country(US)
SF=City(5).join_country(US)

print(US.cities[0].people[0].people_in_my_country())

# 15

モンキーパッチ

この場合、 "monkey patching"とは、定義されたクラスに新しい変数またはメソッドを追加することを意味します。たとえば、クラスAを次のように定義したとします。

class A(object):
    def __init__(self, num):
        self.num = num

    def __add__(self, other):
        return A(self.num + other.num)

しかし、今度はコードの後に​​別の関数を追加したいと思っています。この関数が次のようになっているとします。

def get_num(self):
    return self.num

しかし、これをAメソッドとしてどのように追加しますか?それは単純に、関数を代入文でAに配置するだけです。

A.get_num = get_num

なぜこれは機能しますか?関数は他のオブジェクトとまったく同じオブジェクトなので、メソッドはそのクラスに属する関数です。

関数get_numは、すべての既存の(既に作成されている)ものと、 A新しいインスタンス

これらの追加は、そのクラス(またはそのサブクラス)のすべてのインスタンスで自動的に使用できます。例えば:

foo = A(42)

A.get_num = get_num

bar = A(6);

foo.get_num() # 42

bar.get_num() # 6

他の言語とは異なり、このテクニックは特定の組込み型では機能しません。また、それは良いスタイルとは見なされません。

すべてのクラスメンバーをリストする

dir()関数は、クラスのメンバーのリストを取得するために使用できます。

dir(Class)

例えば:

>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

「非魔法」のメンバーだけを見るのが一般的です。これは、 ____ない名前のメンバーをリストする簡単な理解を使用して行うことができます。

>>> [m for m in dir(list) if not m.startswith('__')]
['append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

警告:

クラスは__dir__()メソッドを定義することができます。そのメソッドが存在する場合、 dir()__dir__() dir()を呼び出します。そうでないと、Pythonはそのクラスのメンバのリストを作成しようとします。これは、dir関数が予期しない結果をもたらす可能性があることを意味します。 公式のPythonドキュメントから重要な2つの引用符:

オブジェクトがdir ()を提供しない場合、関数は、オブジェクトのdict属性(定義されている場合)と型オブジェクトから情報を収集するように最善の努力をします。結果として得られるリストは必ずしも完全なものではなく、オブジェクトがカスタムgetattr ()を持つときは不正確かもしれません。

注意: dir()は、主に対話型プロンプトでの使用の便宜のために提供されているため、厳密にまたは一貫して定義された名前セットを提供しようとするよりも面白い名前セットを提供しようとします。リリース。たとえば、引数がクラスの場合、メタクラス属性は結果リストにありません。

クラスの紹介

クラスは、特定のオブジェクトの基本的な特性を定義するテンプレートとして機能します。ここに例があります:

class Person(object):
     """A simple class."""                            # docstring
     species = "Homo Sapiens"                         # class attribute

     def __init__(self, name):                        # special method
         """This is the initializer. It's a special
         method (see below).
         """
         self.name = name                             # instance attribute

     def __str__(self):                               # special method
         """This method is run when Python tries 
         to cast the object to a string. Return 
         this string when using print(), etc.
         """
         return self.name

     def rename(self, renamed):                       # regular method
         """Reassign and print the name attribute."""
         self.name = renamed
         print("Now my name is {}".format(self.name))

上記の例を見ると、注意すべき点がいくつかあります。

  1. クラスは属性 (データ)とメソッド (関数)で構成されています。
  2. 属性およびメソッドは、単に通常の変数および関数として定義されます。
  3. 対応するdocstringに書かれているように、 __init__()メソッドはイニシャライザと呼ばれます 。他のオブジェクト指向言語のコンストラクタに相当し、新しいオブジェクトやクラスの新しいインスタンスを作成するときに最初に実行されるメソッドです。
  4. クラス全体に適用される属性が最初に定義され、 クラス属性と呼ばれます
  5. クラス(オブジェクト)の特定のインスタンスに適用される属性インスタンス属性と呼ばれます 。それらは一般に__init__()内部で定義されます。これは必須ではありませんが、( __init__()外で定義された属性は、定義される前にアクセスされるリスクがあります)。
  6. クラス定義に含まれるすべてのメソッドは、そのオブジェクトを最初のパラメータとして渡します。 selfという言葉はこのパラメタに使われます( self使用法は実際には慣例によるものですが、 selfという言葉はPythonでは本質的な意味を持ちませんが、これはPythonの最も尊敬される慣習の1つです。
  7. 他の言語でのオブジェクト指向プログラミングに使用されているものは、いくつかの点で驚くかもしれません。ひとつは、Pythonにはprivate要素の概念がないということです。そのため、デフォルトではすべてがC ++ / Java publicキーワードの動作を模倣しています。詳細については、このページの「プライベートクラスメンバー」の例を参照してください。
  8. クラスのメソッドの中には、 __functionname__(self, other_stuff)という形式のものがあります。そのようなメソッドはすべて「マジックメソッド」と呼ばれ、Pythonのクラスの重要な部分です。たとえば、Pythonでの演算子のオーバーロードは、魔法の方法で実装されます。詳細については、関連文書を参照してください。

Personクラスのインスタンスをいくつか作ってみましょう!

>>> # Instances
>>> kelly = Person("Kelly")
>>> joseph = Person("Joseph")
>>> john_doe = Person("John Doe")

現在、3つのPersonオブジェクト、 kellyjoseph 、およびjohn_doeます。

ドット演算子を使用して、各インスタンスからクラスの属性にアクセスできます.ここでもクラス属性とインスタンス属性の違いに注意してください。

>>> # Attributes
>>> kelly.species
'Homo Sapiens'
>>> john_doe.species
'Homo Sapiens'
>>> joseph.species
'Homo Sapiens'
>>> kelly.name
'Kelly'
>>> joseph.name
'Joseph'

同じドット演算子を使ってクラスのメソッドを実行することができ.

>>> # Methods
>>> john_doe.__str__()
'John Doe'
>>>  print(john_doe)
'John Doe'
>>>  john_doe.rename("John")
'Now my name is John'

プロパティ

Pythonクラスは、通常のオブジェクト変数のように見えるプロパティをサポートしますが 、カスタムビヘイビアとドキュメントを添付する可能性があります。

class MyClass(object):

    def __init__(self):
       self._my_string = ""
    
    @property
    def string(self):
        """A profoundly important string."""
        return self._my_string

    @string.setter
    def string(self, new_value):
        assert isinstance(new_value, str), \
               "Give me a string, not a %r!" % type(new_value)
        self._my_string = new_value

    @string.deleter
    def x(self):
        self._my_string = None

オブジェクトのクラスMyClassは、プロパティ.stringを持っているように見えますが、動作は厳密に制御されています。

mc = MyClass()
mc.string = "String!"
print(mc.string)
del mc.string

上記のような便利な構文と同様に、プロパティ構文では、バリデーションやその他の機能拡張をそれらの属性に追加することができます。これは、パブリックAPI(ユーザにレベルのヘルプを提供する必要がある場合)に特に便利です。

プロパティの別の一般的な使用法は、クラスが実際には格納されず、要求されたときにのみ計算される属性である '仮想属性'を提示できるようにすることです。

class Character(object):
    def __init__(name, max_hp):
        self._name = name
        self._hp = max_hp
        self._max_hp = max_hp

    # Make hp read only by not providing a set method
    @property
    def hp(self):
        return self._hp

    # Make name read only by not providing a set method
    @property
    def name(self):
        return self.name

    def take_damage(self, damage):
        self.hp -= damage
        self.hp = 0 if self.hp <0 else self.hp

    @property
    def is_alive(self):
        return self.hp != 0

    @property
    def is_wounded(self):
        return self.hp < self.max_hp if self.hp > 0 else False

    @property
    def is_dead(self):
        return not self.is_alive

bilbo = Character('Bilbo Baggins', 100)
bilbo.hp
# out : 100
bilbo.hp = 200        
# out : AttributeError: can't set attribute
# hp attribute is read only.

bilbo.is_alive
# out : True
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : False

bilbo.take_damage( 50 )

bilbo.hp
# out : 50

bilbo.is_alive
# out : True
bilbo.is_wounded
# out : True
bilbo.is_dead
# out : False

bilbo.take_damage( 50 )
bilbo.hp
# out : 0

bilbo.is_alive
# out : False
bilbo.is_wounded
# out : False
bilbo.is_dead
# out : True

シングルトンクラス

シングルトンは、クラスのインスタンス化を1つのインスタンス/オブジェクトに制限するパターンです。 pythonのシングルトンデザインパターンの詳細については、 こちらを参照してください

class Singleton:
    def __new__(cls):
        try:
            it = cls.__it__
        except AttributeError:
            it = cls.__it__ = object.__new__(cls)
        return it

    def __repr__(self):
        return '<{}>'.format(self.__class__.__name__.upper())

    def __eq__(self, other):
         return other is self

別の方法はあなたのクラスを飾ることです。この答えの例に続いて、シングルトンクラスを作成します。

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Other than that, there are
    no restrictions that apply to the decorated class.

    To get the singleton instance, use the `Instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    Limitations: The decorated class cannot be inherited from.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `Instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)

使用するにはInstanceメソッドを使用できます

@Singleton
class Single:
    def __init__(self):
        self.name=None
        self.val=0
    def getName(self):
        print(self.name)

x=Single.Instance()
y=Single.Instance()
x.name='I\'m single'
x.getName() # outputs I'm single
y.getName() # outputs I'm single


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow