Python学習-カスタムクラス

__slots__変数または__xxx__のような関数名が表示された場合は、それらに注意する必要があります。これらはPythonで特別に使用されます。

__slots__の使用方法はすでに知っています。また、len()メソッドは、クラスがlen()関数に作用できるようにすることでもあります。

さらに、Pythonクラスにはそのような特別な目的の関数がたくさんあり、クラスのカスタマイズに役立ちます。

str

まず、Studentクラスを定義し、インスタンスを出力します。

class Student(object):
	def __init__(self, name):
		self.name = name
>>> print(Student('Michael'))
<__main__.Student object at 0x109afb190>

< main .Student Object AT0x109afb190>の束を印刷します。見栄えがよくありません。

どうすればうまく印刷できますか?__str __()メソッドを定義して、素敵な文字列を返すだけです

class Student(object):
	def __init__(self, name):
		self.name = name
	def __str__(self):
		return 'Student object (name: %s)' % self.name

>>> print(Student('Michael'))
Student object (name: Michael)

印刷された例は見栄えが良いだけでなく、例の中の重要なデータを簡単に見ることができます。
しかし、注意深い友人は、変数を入力するために直接印刷する必要がなく、印刷された例はまだ見栄えが良くないことに気付くでしょう。

>>> s = Student('Michael')
>>> s
<__main__.Student object at 0x109afb310>

これは、直接表示変数が__str __()ではなく__repr __()と呼ばれるためです。この2つの違いは、__ str __()はユーザーに表示される文字列を返すのに対し、__ repr __()はプログラム開発者に表示される文字列を返すことです。つまりrepr()はデバッグ用です。

解決策は、別の__repr __()を定義することです。ただし、通常、__ str __()コードと__repr __()コードは同じであるため、怠惰な記述方法があります。

class Student(object):
	def __init__(self, name):
		self.name = name
	
	def __str__(self):
		return 'Student object (name=%s)' % self.name
	__repr__ = __str__

iter

クラスをリストやタプルなどのfor…inループで使用する場合は、反復可能オブジェクトを返す__iter __()メソッドを実装する必要があります。ただし、Pythonのforループは、引き続き反復可能オブジェクトの_ The _next__を呼び出します。 ()メソッドは、StopIterationエラーが発生したときにループを終了するまで、ループの次の値を取得します。

例としてフィボナッチ数列を取り上げ、forループで使用できるFibクラスを記述してみましょう。

class Fib(object):
	def __init__(self):
		self.a, self.b = 0, 1 # 初始化两个计数器a, b
	
	def __iter___(self):
		return self # 实例本身就是迭代对象,故返回自己

	def __next__(self):
		self.a, self.b = self.b, self.a + self.b
		if self.a > 100000: #退出循环的条件
			raise StopIteration()
		return self.a # 返回下一个值

次に、Fibインスタンスをforループに適用してみます。

>>> for n in Fib():
...     print(n)
...
1
1
2
3
5
...
46368
75025

getitem

Fibインスタンスはforループで使用できますが、リストに少し似ていますが、リストとして使用することはできません。たとえば、5番目の要素を考えてみましょう。

>>> Fib()[5]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Fib' object does not support indexing

添え字要素を抽出するリストのように動作するには、 __ getitem __()メソッドを実装する必要があります。

class Fib(object):
	def __getitem__(self, n):
		a, b = 1, 1
		for x in range(n):
			a, b = b, a + b
		return a

これで、インデックスに従ってシリーズの任意のアイテムにアクセスできます。

>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101

しかし、リストには魔法のスライス方法があります。

>>> list(range(100))[5:10]
[5, 6, 7, 8, 9]

Fibの場合、エラーが報告されました。その理由は、__ getitem __()で渡されるパラメーターがintまたはスライスオブジェクトスライスである可能性があるため、判断する必要があるためです
[:]これは実際にはスライスタイプのオブジェクトであり、すべてのオブジェクトになる価値があります

class Fib(object):
	def __getitem__(self, n):
		if isinstance(n, int): # n是索引
			a, b = 1, 1
			for x in range(n)
				a, b = b, a + b
			return a
		if isinstance(n, slice): # n是切片
			start = n.start
			stop = n.stop
			if start is None:
				start = 0
			a, b = 1, 1
			L = []
			for x in range(stop):
				if x >= start: # 这里注意,必须要这个判断,因为range是从0开始的,但是切片确实从任意开始的
					L.append(a)
				a, b = b, a + b
			return L 

次に、Fibスライスを試してください。

>>> f = Fib()
>>> f[0:5]
[1, 1, 2, 3, 5]
>>> f[:10]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

ただし、stepパラメーターは処理されません。

>>> f[:10:2]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

負の数の処理はないため、__ getitem __()を正しく実装するために行うべき作業はまだたくさんあります。

さらに、オブジェクトがdictと見なされる場合、getitem()のパラメーターは、strなどのキーとして使用できるオブジェクトである場合もあります。

これに対応するのは__setitem __()メソッドで、オブジェクトをリストまたはdictとして扱い、値をセットに割り当てます。最後に、要素を削除するための__delitem __()メソッドがあります。

つまり、上記のメソッドを使用すると、自己定義クラスはPythonに付属するリスト、タプル、および辞書と同じように動作します。これは完全に動的言語の「ダックタイプ」によるものであり、必須ではありません。インターフェイスの継承。
(実際にここで完全な実装を試してみたいのですが、時間などの関係で、実際に使っている場合は振り返り、問題がなければ以下の内容を見てください)

getattr

通常の状況では、クラスのメソッドまたはプロパティを呼び出すときに、それが存在しない場合、エラーが報告されます。たとえば、Studentクラスを定義します。

class Student(object):
    
    def __init__(self):
        self.name = 'Michael'

name属性を呼び出しますが、問題はありませんが、存在しないスコア属性を呼び出すと、問題が発生します。

>>> s = Student()
>>> print(s.name)
Michael
>>> print(s.score)
Traceback (most recent call last):
  ...
AttributeError: 'Student' object has no attribute 'score'

エラーメッセージは、属性スコアが見つからなかったことを明確に示しています。

このエラーを回避するために、Pythonにはスコア属性の追加に加えて、属性を動的に返す__getattr __()メソッドを作成する別のメカニズムがあります以下のように修正します。

class Student(object):
	
	def __init__(self):
		self.name = 'Michael'

	def __getattr__(self, attr):
		if attr=='score':
			return 99

スコアなどの存在しない属性を呼び出す場合、Pythonインタープリターは__getattr __(self、 'score')を呼び出して属性を取得しようとするため、スコアの値を返す機会があります。

>>> s = Student()
>>> s.name
'Michael'
>>> s.score
99

戻り関数も完全に可能です。

class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25

呼び出し元のメソッドのみが次のようになります。

>>> s.age()
25

__getattr__は、属性が見つからない場合にのみ呼び出されることに注意してください。名前などの既存の属性は、__ getattr__で検索されません。

さらに、s.abcなどの呼び出しはNoneを返すことに注意してください。これは、定義した__getattr__のデフォルトの戻り値がNoneであるためです。クラスがいくつかの特定の属性のみに応答するようにするには、規則に従い、AttributeErrorエラーをスローする必要があります。

class Student(object):
	
	def __getattr__(self, attr):
		if attr == 'age':
			return lambda:25
		raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)

実際、クラスのすべての属性とメソッド呼び出しは、特別な手段なしで動的に処理できます。
この動的呼び出し機能の実際的な効果は何ですか?その結果、完全に動的な状況を呼び出すことができます。
例:
現在、多くのWebサイトでSinaWeiboやDoubanなどのRESTAPIが使用されています。APIを呼び出すためのURLは次のとおりです。

  • http://api.server/user/friends
  • http://api.server/user/timeline/list

SDKを作成する場合は、APIに対応するURLごとにメソッドを作成します。これは類似しており、APIを変更したら、SDKを変更する必要があります。

完全に動的な__getattr__を使用して、チェーン呼び出しを記述できます。

class Chain(object):

	def __init__(self, path=''):
		self._path = path

	def __getattr__(selt, path):
		return Chain('%s/%s' % (self._path, path))

	def __str__(self):
		return self._path
	
	__repr__ = __str__

試してみてください:

>>> Chain().status.user.timeline.list
'/status/user/timeline/list'

このように、APIがどのように変更されても、SDKはURLに基​​づいて完全に動的な呼び出しを実装でき、APIの増加に伴って変更されることはありません。

GitHub APIなど、URLにパラメーターを配置するRESTAPIもいくつかあります。

GET /users/:user/repos

呼び出すときは、:userを実際のユーザー名に置き換える必要があります。次のようなチェーン呼び出しを記述できる場合:

Chain().users('michael').repos

APIを非常に便利に呼び出すことができます。

興味のある子供はそれを書き出すことを試みることができます。

ここでは次の__call__が使用されています
**ここで分析すると、__ call__の役割は、実装クラスを関数のように呼び出すことができるということです。**これは非常に重要です。

最初は、@ propertyと@ propertyname.setterを使用し
てここ記述したかったのですが、どちらもメソッドをプロパティに変換してから直接値を割り当てるので、混同しないでください。

class Chain(object):
	def __init__(self, path=''):
		self._path = path
	
	def __getattr__(self, path):
		return Chain('%s/%s' % (self._path, path))
	
	def __call__(self, path):
		return Chain('%s/%s' % (self._path, path))
	
	def __str__(self):
		return self.path
	
	__repr__ = __str__

したがって、Chain()。usersはクラスインスタンス(たとえば、A
、次にA( 'michael')を返し、
__ call__関数を呼び出し、クラスインスタンス、たとえば、B 、次にB.reposを返します。__getattr__は別のクラスを返します。インスタンスC.
印刷が文字列として表示されるのはなぜですか?これは__str__関数によるものです。

私はこの段落をまったく理解していません
振り返ってみると、scoreなどの存在しない属性を呼び出すと、Pythonインタープリターは__getattr __(self、 'score')を呼び出して属性を取得しようとします。この文を参照してください。
明確にするために、属性が存在しない場合、__ getattr__が呼び出され、次に__getattr__がどのように呼び出されるかを確認しますか?
はい、それはインスタンスです。属性(インスタンス名。ポイント属性名)、
Chain()。status.user.timeline.listは
、一連の呼び出しとして理解されます。
__getattr__がどのように定義されているかを確認するにはどうすればよいですか?チェーン(文字列)を
返します。これはクラスインスタンスです。文字列は '%s /%s'%(self._path、path)としてフォーマットされているため、理解されます。では、Chain()。status.user.timeline.listはどのように機能しますか?

 首先
 调用 Chain().status
 那它一调用,就先找类中有定义这个属性嘛?没有,那就找__getattr__函数
 好,找到,这个函数是返回一个Chain(字符串),
 那么,就把_path,就是这个类实例的属性变成了/status

这里关键,然后返回一个类实例Chain,且类属性_path被初始化为了
/status 

 继续Chain().status.user
 一样的,因为本身self._path变成了/status,加上传进来的usr
 根据'%s/%s' % (self._path, path)
 那么_path就变成了/status/usr

这里关键,然后返回一个类实例Chain,且类属性_path被初始化为了
/status/usr

 一直到list

したがって、巧妙なチェーン呼び出しは、クラスインスタンスを継続的に返し、__ init__メソッドを使用してクラスの属性を更新することです。

コール

オブジェクトインスタンスは、独自の属性とメソッドを持つことができます。インスタンスメソッドを呼び出すときは、instance.method()を使用して呼び出します。インスタンス自体で直接呼び出すことはできますか?Pythonでは、答えは常に「はい」です。

どのクラスでも、インスタンスを直接呼び出すには、__ call __()メソッドを定義するだけで済みます。

class Student(object):
	def __init__(self, name):
		self.name = name
	
	def __call__(self):
		print('My name is %s.' % self.name)

呼び出しメソッドは次のとおりです。

>>>s = Student('Michael')
>>>s()
>My name is Michael

call()はパラメータを定義することもできます。インスタンスを直接呼び出すことは、関数を呼び出すことに似ています。したがって、2つの間に基本的な違いがないため、オブジェクトを関数として、関数をオブジェクトとして考えることができます。

オブジェクトを関数と考えると、クラスのインスタンスは実行時に作成されるため、関数自体は実際には許容期間中に動的に作成できます。このような依存関係により、オブジェクトと関数の境界があいまいになります。

では、変数がオブジェクトなのか関数なのかを判断するにはどうすればよいでしょうか。実際、多くの場合、オブジェクトを呼び出すことができるかどうかを判断する必要があります。呼び出すことができるオブジェクトは、関数や上記で定義された__call __()を持つクラスインスタンスなどのCallableオブジェクトです。

>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('str')
False

callable()関数を使用して、オブジェクトが「呼び出し可能」オブジェクトであるかどうかを判別できます。

概要

Pythonクラスでは、多くのカスタムメソッドを定義できるため、特定のクラスを非常に便利に生成できます。

1__str__:紹介のように何をしますか?文字列を
返す2__iter__:クラスを反復可能にし、__ next__メソッドを
記述します3__getitem__:クラスをリストのようにし、インデックスインデックスを使用して値を直接チェックします
4__getattr__:存在しない属性を呼び出すときにこの関数を呼び出します
5__call__:クラスを呼び出します関数のように

おすすめ

転載: blog.csdn.net/qq_44787943/article/details/112541383