[1]:
import typing as T
import attrs
attrs.__version__
[1]:
'21.4.0'
PyCharm Support¶
PyCharm 很早就支持基于 attr.s
API 的自动补全, 但 attrs
是 2021 年之后的新 API, Pycharm 2022.3 中 attrs.define
还不被 PyCharm 所接受.
[2]:
import attr
@attr.s
class Base:
id: int = attr.field()
@attr.s
class User(Base):
name: str = attr.field()
@attr.s
class PaidUser(User):
account: str = attr.field()
# move cursor in bracket and hit CMD + P to see hint
Base()
User()
PaidUser(id="invalid", name="alice", account="1234")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Input In [2], in <cell line: 20>()
16 account: str = attr.field()
19 # move cursor in bracket and hit CMD + P to see hint
---> 20 Base()
21 User()
22 PaidUser(id="invalid", name="alice", account="1234")
TypeError: __init__() missing 1 required positional argument: 'id'
[ ]:
@attr.s
class User:
_id: 1 = attr.ib()
name: str = attr.ib()
user = User(id=1, name="Alice")
print(user)
print(user._id)
[3]:
@attr.s
class Base:
_important_attr: T.List[str] = None
def to_dict(self) -> dict:
return {
k: v
for k, v in attr.asdict(self).items()
if k in self._important_attr
}
@attr.s
class Person(Base):
id: int = attr.ib()
name: str = attr.ib()
_important_attr = ["name"]
person = Person(id=1, name="alice")
person.to_dict()
[3]:
{'name': 'alice'}
Immutable 和 Mutable¶
所谓 Immutable 就是对象一旦被创建, 就无法被修改. 而 Mutable 则是对象被创建后, 其属性值是可以被修改的. 这里要注意的是, 如果对象的属性是 Mutable 的对象, 比如有个属性是列表, 你可以对列表本身进行修改, 但是不能给这个属性赋一个新的值.
Immutable 的好处:
可以从机制上避免很多因为修改带来的错误.
对象可以被缓存, 可以被哈希, 可以用来当 Dict Key, 可以用来去重.
[8]:
def sort_arr(arr: list):
arr.sort()
return arr
@attr.s(frozen=True)
class MyImmutableData:
arr: T.List[int] = attr.ib(factory=list, converter=sort_arr)
my_immutable_data = MyImmutableData(arr=[3, 1, 4, 2])
my_immutable_data.arr.append(5)
print(my_immutable_data.arr)
[1, 2, 3, 4, 5]
Converter¶
很多时候我们的一些属性被传入的时候我们希望对其做一些预处理. 例如将字符串 cast type 成整数, 将列表进行排序等. 而一旦我们的对象是 Mutable 的, Converter 机制就不是很好用了. 因为逻辑上你每次对属性进行修改时就应该运行 converter, 但是你如果用 self.my_attribute = ...
的方式赋值, converter 方法并不会被调用. 所以这时我建议将 Converter 函数都做成这个类的 classmethod, 然后如果你非要调用 self.my_attribute = ...
的时候,
[7]:
def sort_arr(arr: list):
arr.sort()
return arr
@attr.s
class MyMutableData:
arr: T.List[int] = attr.ib(factory=list, converter=sort_arr)
@classmethod
def convert_arr(cls, arr: list) -> list:
return sort_arr(arr)
my_mutable_data = MyMutableData(arr=[1, 2])
my_mutable_data.arr = my_mutable_data.convert_arr([3, 1, 4, 2])
print(my_mutable_data.arr)
[1, 2, 3, 4]
[ ]: