类的成员可以分为三大类:字段、方法和属性
所有成员中,只有普通字段的内容保存对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中只创建一份
字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同
- 普通字段属于对象
- 静态字段属于类
应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段
举个例子,模拟CS游戏场景:
class createRole(object):
ac=None # 类属性/变量, 静态字段
Num=0
def __init__(self,name,role,weapon,lifeValue):
self.Name=name # self.Name 成员属性/变量
self.Role=role
self.Weapon=weapon
self.LifeValue=lifeValue
createRole.Num+=1
def buyWeapon(self,weapon): #类的方法(普通方法)
print('%s is buying [%s]' %(self.Name,weapon))
self.Weapon=weapon
p1=createRole('P1','Police','B10',100)
t1=createRole('T1','Terrorist','B11',100)
p2=createRole('P1','Police','B12',100)
t2=createRole('T2','Terrorist','B13',100)
p1.ac='China-made'
t1.ac='Japan-made'
createRole.ac='US-made'
createRole.Weapon='XD' #创建了一个类中的变量Weapon,并赋值,和实例化后的Weapon变量不同
p1.buyWeapon('AK47') #createRole.buy_weapon(p1,'AK47')
t1.buyWeapon('B51') #和ac一样,buyWeapon并没有在每个类的实例化对象中都创建一遍,所有的实例化对象都共用类中的buyWeapon
print('P1:', p1.Weapon, p1.ac, id(p1.ac))
print('T1:', t1.Weapon, t1.ac, id(t1.ac))
print('P2:', p2.Weapon, p2.ac, id(p2.ac))
print('T2:', t2.Weapon, t2.ac, id(t2.ac))
print(createRole.ac,createRole.Num,id(createRole.ac))
执行结果:
P1 is buying [AK47]
T1 is buying [B51]
P1: AK47 China-made 139965298329840
T1: B51 Japan-made 139965298329904
P2: B12 US-made 139965298317328
T2: B13 US-made 139965298317328
US-made 4 139965298317328
# 可以看到p1.ac,p2.ac语句创建了一个实例变量ac,和类中的ac变量不同
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:至少一个self参数,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量。
- 静态方法:静态方法不可以访问实例变量或类变量,由类调用;通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法
- 类方法:类方法只能访问类变量,不能访问实例变量,由类调用;类方法通过@classmethod装饰器实现
静态方法
修改上个例子加上装饰器如下
@staticmethod
def buyWeapon(self,weapon): #类的方法(静态方法) print('%s is buying [%s]' %(self.Name,weapon))
self.Weapon=weapon
再次执行,结果如下:
TypeError: buyWeapon() missing 1 required positional argument: 'weapon'
分析:当 buyWeapon 变成静态方法后,再通过实例调用时就不会自动把实例本身当作一个参数传给self了。
有两种解决办法:
1、调用时主动传递实例本身给 buyWeapon 方法
p1.buyWeapon(p1,'AK47')
t1.buyWeapon(t1,'B51')
2、在buyWeapon方法中去掉self参数,但这也意味着,在buyWeapon中不能通过self.调用实例中的其它变量了
类方法
修改上个例子加上装饰器如下
@classmethod
def buyWeapon(self,weapon): #类的方法(类方法)
print('%s is buying [%s]' %(self.Name,weapon))
self.Weapon=weapon
再次执行,结果如下:
AttributeError: type object 'createRole' has no attribute 'Name'
分析:name是个实例变量,类方法是不能访问实例变量的,只能访问类变量
@staticmethod
def buyWeapon(self,weapon): #类的方法(类方法) print(self.ac,weapon)
self.Weapon=weapon
再次执行,结果如下:
US-made AK47
US-made B51
P1: B10 China-made 139988741179632
T1: B11 Japan-made 139988741179696
P2: B12 US-made 139988741167120
T2: B13 US-made 139988741167120
US-made 4 139988741167120
对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。方法调用者不同、调用方法时自动传入的参数不同。
属性
属性的作用就是把一个方法变成一个静态属性,其实是普通方法的变种
属性的定义有两种方式:
- 装饰器 即:在方法上应用装饰器
- 静态字段 即:在类中定义值为property对象的静态字段
装饰器方式:在类的普通方法上应用@property装饰器
class Foo: def func(self):
pass
# 定义属性,属性仅有一个self参数
@property
def prop(self):
pass
foo_obj = Foo()
foo_obj.func() #调用方法
foo_obj.prop #调用属性
把一个方法变成静态属性有什么用呢?Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回。
比如 ,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了, 想知道这种状态你必须经历以下几步 :
1. 连接航空公司API查询
2. 对查询结果进行解析
3. 返回结果给你的用户
因此这个status属性的值是一系列动作后才得到的结果,所以你每次调用时,其实它都要经过一系列的动作才返回你结果,但这些动作过程不需要用户关心, 用户只需要调用这个属性就可以。
class Flight(object):Flight
def __init__(self,name):
self.flight_name = name
def checking_status(self):
print("checking flight %s status " % self.flight_name)
return 1
@property
def flight_status(self):
status = self.checking_status()
if status == 0 :
print("flight got canceled...")
elif status == 1 :
print("flight is arrived...")
elif status == 2:
print("flight has departured already...")
else:
print("cannot confirm the flight status...,please check later")
f = Flight("CA980")
f.flight_status
经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
新式类(继承object类)中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object):View Code
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deleter
def price(self):
del self.original_price
obj = Goods()
print(obj.price) # 获取商品价格
obj.price = 200 # 修改商品原价
print(obj.price) # 获取商品价格
del obj.price # 删除商品原价
print(obj.price) # 获取商品价格
执行结果:
80.0
160.0
AttributeError: 'Goods' object has no attribute 'original_price'
静态字段方式,创建值为property对象的静态字段。当使用静态字段的方式创建属性时,经典类和新式类无区别
property的构造方法中有个四个参数
- 第一个参数是方法名,调用
对象.属性
时自动触发执行方法 - 第二个参数是方法名,调用
对象.属性 = XXX
时自动触发执行方法 - 第三个参数是方法名,调用
del 对象.属性
时自动触发执行方法 - 第四个参数是字符串,调用
对象.属性.__doc__
,此参数是该属性的描述信息
class Goods(object):View Code
def __init__(self):
self.original_price = 100 # 原价
self.discount = 0.8 # 折扣
def get_price(self):
new_price = self.original_price * self.discount # 实际价格 = 原价 * 折扣
return new_price
def set_price(self, value):
self.original_price = value
def del_price(self):
del self.original_price
#PRICE = property(get_price, set_price, del_price, '价格属性描述...')
PRICE = property(get_price, set_price, del_price, '123')
obj = Goods()
print(obj.PRICE) # 获取商品价格
obj.PRICE = 200 # 修改商品原价
print(obj.PRICE) # 获取商品价格
print(obj.PRICE.__doc__)
del obj.PRICE # 删除商品原价
print(obj.PRICE) # 获取商品价格
不知道这里为什么获取不到doc
Python WEB框架 Django 的视图中 request.POST 就是使用的静态字段的方式创建的属性