属性:通过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),也可以设值不同的函数来设值特性的不同方式。