Python Language
クラス
サーチ…
前書き
Pythonは一般的なスクリプティング言語としてだけでなく、オブジェクト指向プログラミングパラダイムもサポートしています。クラスはデータを記述し、そのデータを操作するメソッドを提供します。これらのメソッドはすべて単一のオブジェクトの下にあります。さらに、クラスでは、具体的な実装の詳細をデータの抽象的な表現から分離することによって、抽象化が可能です。
クラスを利用するコードは、一般的に読みやすく、理解し、維持するのがより簡単です。
基本的な継承
Pythonの継承は、Java、C ++などの他のオブジェクト指向言語で使用されている同様の考え方に基づいています。新しいクラスは、既存のクラスから次のように派生することができます。
class BaseClass(object):
pass
class DerivedClass(BaseClass):
pass
BaseClass
はすでに存在する( 親 )クラスで、 DerivedClass
はBaseClass
から属性を継承する(またはサブクラス化する)新しい( 子 )クラスです。 注意 :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
になります。
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__
。
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では区別されています。
import inspect
inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
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.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では、古いスタイルのクラスが削除されました。
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アルゴリズムは
- 深さは最初に(例えば
FooBar
、Foo
) - 共有親(
object
)子(によってブロックされているBar
)と - 循環的な関係は許されません。
つまり、たとえば、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__
これらは、インスタンス上の点線ルックアップを制御することができ、関数、 staticmethod
、 classmethod
、およびproperty
を実装するために使用されproperty
。点線のルックアップ(例えば、 Foo
クラスのインスタンスfoo
は、属性bar
探していbar
- つまりfoo.bar
)は以下のアルゴリズムを使用します:
bar
はクラスFoo
で検索されます。それはそこにあり、それがデータ記述子である場合には、データ記述子が使用されています。これはproperty
がインスタンス内のデータへのアクセスを制御する方法であり、インスタンスはこれをオーバーライドできません。 データ記述子が存在しない場合は、bar
はインスタンス__dict__
で検索されます。このため、インスタンスから呼び出されるメソッドを点線のルックアップでオーバーライドまたはブロックすることができます。インスタンス内にbar
が存在する場合は、そのインスタンスが使用されます。そうでなければ、我々はクラス
Foo
でbar
探しbar
。 Descriptorの場合、記述子プロトコルが使用されます。これは、関数(このコンテキストでは、バインドされていないメソッド)、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つの大きな問題があります。
パラメータの
first_name
とlast_name
あなたが完全な名前を入力することができるので、今紛らわしいですfirst_name
。また、この種の柔軟性を持つより多くのケースやパラメータがあれば、if / elif / elseブランチングは厄介なものになります。それほど重要ではありませんが、それでも指摘する価値があります:もし
last_name
がNone
であれば、first_name
はスペースで2つ以上のものに分割されませんか?入力の検証や例外処理の別の層があります...
クラスメソッドを入力します。単一のイニシャライザをfrom_full_name
するのではfrom_full_name
、 from_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
代わりにcls
をfrom_full_name
ます。クラスメソッドは、与えられたクラスのインスタンスではなく 、全体のクラスに適用されます(これは、通常、 self
が意味するものです)。したがって、 cls
がPerson
クラスである場合、 from_full_name
クラスメソッドから返される値はPerson(first_name, last_name, age)
であり、 Person
クラスのインスタンスを作成するためにPerson
の__init__
を使用します。特に、 Employee
of Person
サブクラスを作成する場合、 from_full_name
はEmployee
クラスでも動作します。
これが期待どおりに動作することを示すために、 __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.
その他の参考資料:
https://docs.python.org/2/library/functions.html#classmethod
https://docs.python.org/3.5/library/functions.html#classmethod
クラス構成
クラス構成は、オブジェクト間の明示的な関係を可能にする。この例では、人々は国に属する都市に住んでいます。構成によって、国に住むすべての人の数にアクセスできます。
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))
上記の例を見ると、注意すべき点がいくつかあります。
- クラスは属性 (データ)とメソッド (関数)で構成されています。
- 属性およびメソッドは、単に通常の変数および関数として定義されます。
- 対応するdocstringに書かれているように、
__init__()
メソッドはイニシャライザと呼ばれます 。他のオブジェクト指向言語のコンストラクタに相当し、新しいオブジェクトやクラスの新しいインスタンスを作成するときに最初に実行されるメソッドです。 - クラス全体に適用される属性が最初に定義され、 クラス属性と呼ばれます 。
- クラス(オブジェクト)の特定のインスタンスに適用される属性はインスタンス属性と呼ばれます 。それらは一般に
__init__()
内部で定義されます。これは必須ではありませんが、(__init__()
外で定義された属性は、定義される前にアクセスされるリスクがあります)。 - クラス定義に含まれるすべてのメソッドは、そのオブジェクトを最初のパラメータとして渡します。
self
という言葉はこのパラメタに使われます(self
使用法は実際には慣例によるものですが、self
という言葉はPythonでは本質的な意味を持ちませんが、これはPythonの最も尊敬される慣習の1つです。 - 他の言語でのオブジェクト指向プログラミングに使用されているものは、いくつかの点で驚くかもしれません。ひとつは、Pythonには
private
要素の概念がないということです。そのため、デフォルトではすべてがC ++ / Javapublic
キーワードの動作を模倣しています。詳細については、このページの「プライベートクラスメンバー」の例を参照してください。 - クラスのメソッドの中には、
__functionname__(self, other_stuff)
という形式のものがあります。そのようなメソッドはすべて「マジックメソッド」と呼ばれ、Pythonのクラスの重要な部分です。たとえば、Pythonでの演算子のオーバーロードは、魔法の方法で実装されます。詳細については、関連文書を参照してください。
Person
クラスのインスタンスをいくつか作ってみましょう!
>>> # Instances
>>> kelly = Person("Kelly")
>>> joseph = Person("Joseph")
>>> john_doe = Person("John Doe")
現在、3つのPerson
オブジェクト、 kelly
、 joseph
、および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