属性:通过obj.attr
的方式获取,称为属性。
特性:有点函数味道的属性。可以通过@property
装饰器,或者property()
函数设置特性。
1
2
3
4
5
6
7
8
9
10
11
| class Circle(object):
def __init__(self, radius):
self.radius = radius # 属性
def getdiameter(self):
return self.radius * 2
def setdiameter(self, new_diameter):
self.radius = new_diameter / 2
diameter = property(getdiameter, setdiameter) # 特性
|
与属性相关的方法
__getattr__
方法,如方法名所述,是获取属性的方法,当在实例、类或超类中找不到指定的属性时,解释器才会调用特殊的__getattr__
方法获取属性。
hasattr(obj, name)
函数,如果obj对象中存在指定的属性,或者能以某种方式通过obj对象获取指定的属性,返回true。
setattr(obj, name, value)
函数,把obj对象指定属性的值设置为value,前提是obj对象能接受这个值;这个函数可能会创建一个新的属性或者覆盖现有的属性。
__dict__
属性,存储对象或类的可写属性,有dict属性的对象,任何时候都能设置新属性。
__slots__
属性,类可以定义这个属性,限制实例能有哪些属性。
dir([object])
方法,列出对象的大部分属性。
vars([obj])
方法,返回obj对象的__dict__属性,如果实例所属的类定义了__slots__属性,实例没有__dict__属性,那么vars函数不能处理那个实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| class Info(object):
__slots__ = ("name", "__data", "allowattr")
def __init__(self, mappin g):
self.name = "test_info"
self.__data = dict(mapping)
def __getattr__(self, item):
print("Find attribute {} through __getattr__.".format(item))
if item in self.__data.keys():
return self.__data[item]
else:
return "ValueError"
info = Info(dict(radius=1, diameter=2))
print(info.name) # 通过属性访问
>>> test_info
print(info.radius) # 通过__getattr__方法访问
>>> Find attribute radius through __getattr__.
>>> 1
print(info.area) # 通过__getattr__方法访问
>>> Find attribute area through __getattr__.
>>> ValueError
print(hasattr(info, "__data")) # 通过hasattr获取属性
>>> True
setattr(info, "name", "name_change") # 通过setattr设置属性
print(info.name)
>>> name_change
print(info.__dict__) # 通过__dict__获取实例的所有属性,当类中没有使用__slots__方法时可用!
>>> {'name': 'name_change', '_Info__data': {'radius': 1, 'diameter': 2}}
info.allowattrfangfa = 4
print(info.allfangfaowattr)
>>> 4
fangfa
info.notallowafangfattr = 5 # 设置不允许的属性抛出异常
>>> AttributeEfangfarror: 'Info' object has no attribute 'notallowattr'
print(dir(info))
>>> ['_Info__data', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'allowattr', 'name']
print(vars(info)) # 当类中未设置__slots__属性时有用
>>> {'name': 'name_change', '_Info__data': {'radius': 1, 'diameter': 2}, 'allowattr': 4, 'notallowattr': 5}
|
__new__方法
__init__
称为构造方法,但是,在python中构建实例的是特殊方法__new__
。
__new__
方法的一般形式为__new__(cls, *args, **kwargs)
。
__new__
方法的返回值为实例,会作为第一个参数(即self)传给__init__
方法。
调用__init__
方法时需要传入实例,而且禁止返回任何值
!!
所以,__init__
方法其实是初始化方法,类的属性通常在__init__
方法中初始化,真正的构造方法是__new__
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| class Foo(object):
def __new__(cls, x, *args, **kwargs):
self = object.__new__(cls) # 实例化需先调用父类的实例化方法__new__
self.x = x
return self
def __init__(self, *args, **kwargs): # 初始化方法,self中已经添加了属性x
print("vars(self) ->", vars(self))
self.args = args
self.kwargs = kwargs
foo = Foo(10, "hello", kwargs="world")
>>> vars(self) -> {'x': 10}
|
从上面的例子可以看到,在实例初始化时,先是调用了__new__
方法,然后才调用的__init__
方法。
特性的使用
特性的使用方法有两种:使用装饰器
或者使用property()
函数定义。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| class LineItem:
def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price
def SubTotal(self):
return self.weight * self.price
@property
def weight(self): # 必须与属性(Attribute)名称相同
return self.__weight
@weight.setter # 装饰器将读值方法与设值方法绑定在一起
def weight(self, value):
if value > 0:
self.__weight = value
else:
raise ValueError("value must be > 0")
def GetPrice(self):
return self.__price
def SetPrice(self, value):
if value > 0:
self.__price = value
else:
raise ValueError("value must be > 0")
price = property(GetPrice, SetPrice)
raisins = LineItem('Golden raisins', 10, 6.0)
print((raisins.weight, raisins.price))
>>> (10, 6.0)
|
在使用装饰器来定义特性时,可以使用@prepertyname.setter
装饰器来设值特性的设值方式,@prepertyname.deleter
装饰器来设值del命令执行的行为。
通过特性工厂函数property(fget=None, fset=None, fdel=None, doc=None)
,也可以设值不同的函数来设值特性的不同方式。
Author
Alfons
LastMod
2018-08-30
License
Creative Commons BY-NC-ND 3.0