简介:
这个dataclasses是当做装饰器来用,作用是在我们定义数据class对象时减少我们的代码量。
文章主要是用IDE写的,还请见谅。
?
正文:
from dataclasses import dataclass
#带上这个装饰器帽子,相当于它的init,repr,eq等双下划线方法都自动帮我们创建
@dataclass(init=True, repr=True, eq=True, order=True,unsafe_hash=False, frozen=False)
class Point:
x: float
__y: float #外部不可访问和更改
z: float = 0.0
# def __repr__(self): #你也可以自己重写魔术方法,它将是最终生效的那个
# return "test"
p = Point(1.1,2.2) #加了装饰器可以直接进行赋值使用
p.__y = 111 #无法修改
print(p) #Point(x=1.1, _Point__y=2.2, z=0.0)
'''
戴帽子的时候也可以这样写:
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
是的,你可以决定哪些魔术方法需要装饰器帮你自动添加到class中,注意它们的默认值。
init,repr这两个就不说了,__eq__这个方法:
它将class当做一个填充有字段的元组来比较是否相等,而且要求字段名,值,顺序,类型全部一致才算相等。
'''
p1 = Point(1.1,2.2)
p2 = Point(z=2.2,x=1.1,_Point__y=0) #改变了形参位置
print(p==p1, p==p2) #true false
'''
order=True的话,那么__lt__,__le__,__gt__,ge__也就是,>=这些魔术方法都会生成到你的class中
#如果False,则定义的class不支持比较,会抛异常,除了==
默认的比较规则貌似是只比较第一个参数,你最好自己重写这些方法
'''
@dataclass(order=True)
class Point_1:
x: float
y: int
p3=Point_1(0.0, 1)
p4=Point_1(0.1, 0)
print(p3
'''
frozen=True的话,class的实例不支持修改,即instance.field=xxx 都是不支持的
'''
@dataclass(frozen=True)
class Point_2:
x: float
y: int
z: dict
# def __setattr__(self, key, value): #若frozen=true,连这个setattr方法都不允许定义(getattr也不准,参照https://www.python.org/dev/peps/pep-0557/#specification)
# self.z[key] = value
def __getattr__(self, item): #不过这里并没有抛出异常
return self.item
# def __delattr__(self, item): #同样,它也不给定义
# del self.y
p3=Point_2(0.0, 1, {})
# p3.y = 1 #dataclasses.FrozenInstanceError: cannot assign to field 'y'
print(p3.z) #居然是可以访问的
'''
frozen: If true (the default is False), assigning to fields will generate an exception.
This emulates read-only frozen instances. If either __getattr__ or __setattr__ is defined in the class, then ValueError is raised
原话说的是模仿一个 只读的实例,__getattr__和__setattr__都不允许自定义,否则抛异常。
个人认为__setattr__不允许自定义是ok的,__getattr__不允许那还怎么读呢,所以这里应该是表述有问题。
'''
'''
unsafe_hash默认是false,但它会根据eq和frozen参数来进行设置
若eq和frozen都是true,那么__hash__方法将会添加到class中
若eq=true,frozen=false,那么__hash__=None, 且标记class为unhashable的类
若eq=false,将保持原来的__hash__方法,意思是使用class父类的哈希方法,python3中默认类都是继承自object类(或者继承了别的类)。
'''
@dataclass(frozen=True)
class Point_3:
x: float
p4 = Point_3(1)
print(p4.__hash__) # 标明此类是有hash方法的
print(p4.__hash__()) # 3430019387558 调用其hash方法,等效于hash(p4)
print(hash(p4) == p4.__hash__())
@dataclass(frozen=False,eq=True) #这个时候__hash__=None
class Point_4:
x: float
p5 = Point_4(1)
print(p5.__hash__) #None
# print(p5.__hash__()) #'NoneType' object is not callable
'''
##关于field()方法##
有时候在数据类中我们需要给一些字段附加一些额外的信息,
如a这个字段我要定死,不给它走init,在repr中也不希望被显示出来,还不希望拿来比较
'''
from dataclasses import field
@dataclass(unsafe_hash=True) #强制生成__hash__方法
class Point_5:
x: float
y: int = field(default=10,init=False,repr=False)
z: str = field(default="", compare=False) #比较时忽略此字段
f: str = field(default="", hash=False) #true的话,hash后的结果是不一样的
p6 = Point_5(x=1, z="A") #不再允许被设置
print(p6.y) #10 仍然可以访问
print(p6) #Point_5(x=1) 显示的查看对象也看不到y的值了
p7 = Point_5(x=1, z="B")
print(p6==p7) #true z被忽略了,只比较了x
print(hash(p7))
#使用default_factory参数来为字段设置默认值,他必须是一个可调用的对象,比如func,class
#要注意它和default是不相容的,有你没我,定义时只能使用两者中一个参数
def generate_x():
return 1
@dataclass
class Point_6:
x: float = field(default_factory=generate_x) #
y: float = field(default_factory=dict) #list, str, int...
p8 = Point_6()
print(p8) #Point_6(x=1)
如上已经介绍了大部分dataclasses库的用法,更多关于它的使用参考
python3.7的更新参考