【廖雪峰python進級ノート】カスタムクラス
28712 ワード
1. __str__ および_repr__
クラスのインスタンスをstrにするには、特別な方法
__str__()
を実装する必要があります.class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __str__(self):
return '(Person: %s, %s)' % (self.name, self.gender)
インタラクティブコマンドラインでprintを使用して試します.
>>> p = Person('Bob', 'male')
>>> print p
(Person: Bob, male)
ただし、変数pを直接叩くと:
>>> p
object at 0x10c941890>
str()は呼び出されないようです.
Pythonは
__str__()
と__repr__()
の2つの方法を定義しているため、__str__()
はユーザーに表示され、__repr__()
は開発者に表示される.サボる定義
__repr__
の方法があります.class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __str__(self):
return '(Person: %s, %s)' % (self.name, self.gender)
__repr__ = __str__
2. __cmp__
int、strなどの内蔵データ型をソートする場合、Pythonの
sorted()
はデフォルトの比較関数cmp
に従ってソートされますが、Studentクラスの
のセットをソートする場合は、独自の特別な方法__cmp__()
を提供する必要があります.class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return '(%s: %s)' % (self.name, self.score)
__repr__ = __str__
def __cmp__(self, s):
if self.name < s.name:
return -1
elif self.name > s.name:
return 1
else:
return 0
上記のStudentクラスは
__cmp__()
メソッドを実装し、__cmp__
はインスタンス自体selfと入力されたインスタンスsとを比較し、selfが前に並ぶべきであれば−1を返し、sが前に並ぶべきであれば1を返し、両者が等しい場合は0を返す.Studentクラスはnameでソートされます.
>>> L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 77)]
>>> print sorted(L)
[(Alice: 77), (Bob: 88), (Tim: 99)]
注意:listにStudentクラスだけが含まれていない場合は、
__cmp__
がエラーを報告する可能性があります.L = [Student('Tim', 99), Student('Bob', 88), 100, 'Hello']
print sorted(L)
解決策を考えてください.
例:Studentの
__cmp__
メソッドを変更して、スコアが高い順に並べ替え、スコアが同じ順に名前で並べ替えてください.
はscoreを先に比較し、scoreが等しい場合、nameを比較する必要がある.参照コード:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return '(%s: %s)' % (self.name, self.score)
__repr__ = __str__
def __cmp__(self, s):
if self.score > s.score:
return -1
elif self.score == s.score:
if self.name > s.name:
return 1
elif self.name == s.name:
return 0
else:
return -1
else:
return 1
L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 99)]
print sorted(L)
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return '(%s: %s)' % (self.name, self.score)
__repr__ = __str__
def __cmp__(self, s):
if self.score == s.score:
return cmp(self.name, s.name)
return -cmp(self.score, s.score)
L = [Student('Tim', 99), Student('Bob', 88), Student('Alice', 99)]
print sorted(L)
3. __len__
クラスがlistのように表現されている場合、要素がいくつあるかを取得するには、
len()
関数を使用します.len()関数を正常に動作させるには、クラスは要素の数を返す特別な方法
__len__()
を提供する必要があります.たとえば、Studentsクラスを書いて、名前を伝えます.
class Students(object):
def __init__(self, *args):
self.names = args
def __len__(self):
return len(self.names)
__len__()
メソッドが正しく実装されている限り、studentsインスタンスの「長さ」をlen()関数で返すことができます.>>> ss = Students('Bob', 'Alice', 'Tim')
>>> print len(ss)
3
例のフィボナッチ数列は0,1,1,2,3,5,8…から構成される.
Fibクラスを作成してください.Fib(10)は数列の上位10要素を表し、print Fib(10)は数列の上位10要素を印刷することができ、len(Fib(10))は数列の個数10を正しく返すことができます.
はnumからフィボナッチ数列の最初のN要素を計算する必要がある.参照コード:
class Fib(object):
def __init__(self, num):
a, b, L = 0, 1, []
for n in range(num):
L.append(a)
a, b = b, a + b
self.numbers = L
def __str__(self):
return str(self.numbers)
__repr__ = __str__
def __len__(self):
return len(self.numbers)
f = Fib(10)
print f
print len(f)
4.数学演算
Pythonが提供する基本データ型int,floatは,整数と浮動小数点の4則演算や乗方などの演算を行うことができる.
しかしながら、
はintやfloatに限らず、有理数、行列等であってもよい.有理数を表すには、
Rational
で表すことができます.class Rational(object):
def __init__(self, p, q):
self.p = p
self.q = q
p,qはいずれも整数であり,
p/q
を表す.Rationalを+演算するには、
__add__
を正しく実装する必要があります.class Rational(object):
def __init__(self, p, q):
self.p = p
self.q = q
def __add__(self, r):
return Rational(self.p * r.q + self.q * r.p, self.q * r.q)
def __str__(self):
return '%s/%s' % (self.p, self.q)
__repr__ = __str__
有理数加算を試してみましょう.
>>> r1 = Rational(1, 3)
>>> r2 = Rational(1, 2)
>>> print r1 + r2
5/6
インスタンスRationalクラスは加算は可能ですが、減算、乗算、除算はできません.Rationalクラスを改善し続け、4つの演算を実現してください.
ヒント:減算:
__sub__
乗算:__mul__
除算:__div__
演算結果が6/8の場合、表示時に最も簡単な形式3/4にまとめる必要があります.参照コード:
def gcd(a, b):
if b == 0:
return a
return gcd(b, a % b)
class Rational(object):
def __init__(self, p, q):
self.p = p
self.q = q
def __add__(self, r):
return Rational(self.p * r.q + self.q * r.p, self.q * r.q)
def __sub__(self, r):
return Rational(self.p * r.q - self.q * r.p, self.q * r.q)
def __mul__(self, r):
return Rational(self.p * r.p, self.q * r.q)
def __div__(self, r):
return Rational(self.p * r.q, self.q * r.p)
def __str__(self):
g = gcd(self.p, self.q)
return '%s/%s' % (self.p / g, self.q / g)
__repr__ = __str__
r1 = Rational(1, 2)
r2 = Rational(1, 4)
print r1 + r2
print r1 - r2
print r1 * r2
print r1 / r2
5.タイプ変換
Rationalクラスは有理数演算を実現していますが、結果をintやfloatに変えるにはどうすればいいですか?
整数と浮動小数点数の変換を考察する:
>>> int(12.34)
12
>>> float(12)
12.0
Rationalをintに変更する場合は、次の操作を行います.
r = Rational(12, 5)
n = int(r)
int()関数を正常に動作させるには、特別な方法
__int__()
を実装する必要があります.class Rational(object):
def __init__(self, p, q):
self.p = p
self.q = q
def __int__(self):
return self.p // self.q
結果は次のとおりです.
>>> print int(Rational(7, 2))
3
>>> print int(Rational(1, 3))
0
同様に、float()関数を正常に動作させるには、特殊な方法
__float__()
を実現する必要がある.def __float__(self):
return float(self.p) / self.q
#
def __float__(self):
return 1.0 * self.p / self.q
6. @property
スタディクラスの考察:
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
Studioのscroeプロパティを変更する場合は、次のように書きます.
s = Student('Bob', 59)
s.score = 60
しかし、このように書くこともできます.
s.score = 1000
属性に直接値を割り当てると、スコアの有効性がチェックされないことは明らかです.
2つの方法を使用した場合:
class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
def get_score(self):
return self.__score
def set_score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self.__score = score
これでs.set_score(1000)はエラーを報告します.
この
get/set
手法を用いて1つの属性へのアクセスをカプセル化することは、多くのオブジェクト向けプログラミング言語でよく見られる.でもs.get_と書きますscore()とs.set_score()はs.scoreを直接書いていません.
両方を完璧にする方法はありますか?—あります.
Pythonは高次関数をサポートするため、関数式プログラミングでは
を紹介し、get/setメソッドを装飾器関数で属性呼び出しに「装飾」することができます.class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self.__score = score
注意:最初のscore(self)はgetメソッド、@propertyで装飾、2番目のscore(self,score)はsetメソッド、@score.setter装飾,@score.setterは前の@property装飾後の副産物です.
プロパティを使用するようにscoreを設定できます.
>>> s = Student('Bob', 59)
>>> s.score = 60
>>> print s.score
60
>>> s.score = 1000
Traceback (most recent call last):
...
ValueError: invalid score
scoreに値を割り当てる実際に呼び出されたのはsetメソッドであることを説明します.
インスタンスsetメソッドを定義しないと、プロパティに値を割り当てることができません.この場合、読み取り専用のプロパティを作成できます.
Studentクラスにgradeプロパティを追加し、scoreに基づいてA(>=80)、B、C(<60)を計算してください.
@propertyでgradeを修飾するgetメソッドで読み取り専用属性を実現できます.参照コード:
class Student(object):
def __init__(self, name, score):
self.name = name
self.__score = score
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
if score < 0 or score > 100:
raise ValueError('invalid score')
self.__score = score
@property
def grade(self):
if self.score < 60:
#self.__score < 60
return 'C'
if self.score < 80:
return 'B'
return 'A'
s = Student('Bob', 59)
print s.grade
s.score = 60
print s.grade
s.score = 99
print s.grade
7. __slots__
Pythonは動的言語であるため、どのインスタンスも実行期間中に動的に属性を追加できます.
に追加する属性、例えば、Studentクラスがname、gender、scoreの3つの属性のみを追加できる場合、Pythonの特殊な__slots__
を利用して実現することができる.名前の通り、
__slots__
はクラスが許可する属性のリストを指します.class Student(object):
__slots__ = ('name', 'gender', 'score')
def __init__(self, name, gender, score):
self.name = name
self.gender = gender
self.score = score
次に、インスタンスを操作します.
>>> s = Student('Bob', 'male', 59)
>>> s.name = 'Tim' # OK
>>> s.score = 99 # OK
>>> s.grade = 'A'
Traceback (most recent call last):
...
AttributeError: 'Student' object has no attribute 'grade'
__slots__
の目的は、現在のクラスが持つことができる属性を制限することであり、任意のダイナミックな属性を追加する必要がなければ、__slots__
を使用してもメモリを節約できます.8. __call__
Pythonでは、関数は実際にはオブジェクトです.
>>> f = abs
>>> f.__name__
'abs'
>>> f(-123)
123
fは呼び出すことができるので、fは呼び出すことができるオブジェクトと呼ばれる.
すべての関数は呼び出し可能なオブジェクトです.
1つのクラスインスタンスは、1つの特別な方法
を実装するだけで__call__()
になることもできる.Personクラスを呼び出し可能なオブジェクトにしました.
class Person(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
def __call__(self, friend):
print 'My name is %s...' % self.name
print 'My friend is %s...' % friend
Personインスタンスを直接呼び出すことができます.
>>> p = Person('Bob', 'male')
>>> p('Tim')
My name is Bob...
My friend is Tim...
p(‘Tim’)を見ると、pが関数なのかクラスインスタンスなのか分からないので、Pythonでは関数もオブジェクトであり、オブジェクトと関数の違いは顕著ではありません.