Closed11
Fluent Python 読書メモ

19章 動的属性とプロパティ

obj.data
の検索順
-
obj.__class__
のdata
という名前の プロパティ obj
obj.__class__
-
obj.__class__
の親クラス

>>> class Foo:
... @property
... def bar(self):
... '''The bar attribute'''
... return self.__dict__['bar']
...
... @bar.setter
... def bar(self, value):
... self.__dict__['bar'] = value
...
>>> help(Foo.bar)
Help on property:
bar
The bar attribute
下のコードと等価
>>> class Hoge:
... fuga = property(
... lambda self: self.__dict__["fuga"],
... lambda self, value: self.__dict__.update({"fuga": value}),
... doc="The fuga attribute",
... )
...
>>> help(Hoge.fuga)
Help on property:
fuga
The fuga attribute

weight = quantity("weight")
と 2 回 weight
を書かないといけないのが冗長。
>>> def quantity[T](storage_name: str) -> property:
... def qty_getter(instance: T) -> int:
... return instance.__dict__[storage_name]
...
... def qty_setter(instance: T, value: int):
... if value > 0:
... instance.__dict__[storage_name] = value
... else:
... raise ValueError("value must be > 0")
...
... return property(qty_getter, qty_setter)
...
...
... class LineItem:
... weight = quantity("weight")
... price = quantity("price")
...
... def __init__(self, description: str, weight: int, price: int) -> None:
... self.description = description
... self.weight = weight
... self.price = price
...
... def subtotal(self) -> int:
... return self.weight * self.price
...
...
>>> apple = LineItem("apple", 10, 1)
>>> apple.weight
10
>>> apple.weight = 11
>>> apple.weight
11
>>> apple.weight = -11
Traceback (most recent call last):
File "<python-input-4>", line 1, in <module>
apple.weight = -11
^^^^^^^^^^^^
File "<python-input-0>", line 9, in qty_setter
raise ValueError("value must be > 0")
ValueError: value must be > 0

__getattribute__
によって属性を取得しようとしたときの振る舞いを定義できる。(ドット表記、getattr()
, hasattr()
)
__getattr__
は、__getattribute__
の呼び出し中に AttributeError
が投げられると呼ばれる。
>>> class Sample:
... def __getattribute__(self, name: str):
... print("called __getattribute__")
...
... raise AttributeError
...
... def __getattr__(self, name: str):
... print("called __getattr__")
...
...
>>> Sample().name
called __getattribute__
called __getattr__

20章 属性ディスクリプタ

ディスクリプタとは、同じアクセスロジックを複数の属性で再利用する方法です。
import abc
class AutoStorage[T]:
__counter = 0
def __init__(self):
cls = self.__class__
prefix = cls.__name__
index = cls.__counter
self.storage_name = f"_{prefix}#{index}"
cls.__counter += 1
def __get__(self, instance: object, owner):
if instance is None:
return self
else:
return getattr(instance, self.storage_name)
def __set__(self, instance: object, value: T):
setattr(instance, self.storage_name, value)
class Validated[T](abc.ABC, AutoStorage[T]):
def __set__(self, instance: object, value: T):
value = self.validate(instance, value)
super().__set__(instance, value)
@abc.abstractmethod
def validate(self, instance: object, value: T) -> T:
"""return validated value or raise ValueError"""
class Quantity(Validated[int]):
def validate(self, instance: object, value: int) -> int:
if value <= 0:
raise ValueError("value must be > 0")
return value
class NonBlank(Validated[str]):
def validate(self, instance: object, value: str) -> str:
value = value.strip()
if len(value) == 0:
raise ValueError("value cannot be empty or blank")
return value

このアサーションは失敗する
class Sample:
def f(self):
pass
assert Sample().f is Sample.f
Sample().f
の方は自動的に第一引数に Sample()
が渡されることを考えれば驚くこともない。

メソッドは非オーバーライドディスクリプタ。

特殊メソッドにおいては、インタプリタはクラスそのものからメソッドを検索する。
repr(x)
<=> x.__class__.__repr__(x)

21章 クラスメタプログラミング
このスクラップは2025/01/12にクローズされました