Python中的属性(字段)、方法和特性(装饰器)
类属性和实例属性
类属性:类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本。对于公有的类属性,在类外可以通过类对象和实例对象访问。
实例属性:实例属性就是实例对象所拥有的属性。
class People(object):
country = 'name' #类属性
print(People.country)
p = People() # 创建一个实例对象
print(p.country)
p.country = 'xiaoke'
print(p.country) #实例属性会屏蔽掉同名的类属性
print(People.country)
del p.country #删除实例属性
print(p.country)
name
name
xiaoke
name
name
小结
-
如果需要在类外修改类属性,必须通过实例对象去调用类方法中设置属性方法进行修改。
-
如果通过实例对象去引用,会产生一个同名的实例属性,这种方式修改的是实例属性,不会影响到类属性,并且之后如果通过实例对象去引用该名称的属性,实例属性会强制屏蔽掉类属性,即引用的是实例属性,除非删除了该实例属性。
-
但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。
类方法、实例方法和静态方法
类方法
类方法是类对象所拥有的方法,需要用装饰器"@classmethod"来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以"cls"作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以’cls’作为第一个参数的名字,就最好用’cls’了),能够通过实例对象和类对象去访问。
class People:
country = "China"
# 类方法
@classmethod
def getCountry(cls):
return cls.country
p = People()
print(People.getCountry())
print(p.getCountry())
China
China
类方法还有一个用途就是可以对类属性进行修改。
class People:
country = 'china'
#类方法,用classmethod来进行修饰
@classmethod
def getCountry(cls):
return cls.country
@classmethod
def setCountry(cls,country):
cls.country = country
p = People()
print(p.getCountry()) #可以用实例对象引用
print(People.getCountry()) #可以用类对象引用
p.setCountry('japan')
print(p.getCountry())
print(People.getCountry())
china
china
japan
japan
实例方法
实例方法在类中最常定义的成员方法,它至少有一个参数并且必须以实例对象作为其第一个参数,一般以名为’self’的变量作为第一个参数(当然可以以其他名称的变量作为第一个参数)。在类外实例方法只能通过实例对象去调用,不能通过其他方式去调用。
class People:
country = 'China'
#实例方法
def getCountry(self):
return self.country
p = People()
# p.country = "Japan"
print(p.getCountry()) #正确,可以用过实例对象引用
print(People.getCountry()) #错误,不能通过类对象引用实例方法
China
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-cb0faf0872a3> in <module>()
12 # p.country = "Japan"
13 print(p.getCountry()) #正确,可以用过实例对象引用
---> 14 print(People.getCountry()) #错误,不能通过类对象引用实例方法
TypeError: getCountry() missing 1 required positional argument: 'self'
静态方法
静态方法需要通过修饰器"@staticmethod"来进行修饰,静态方法不需要多定义参数。
class People:
country = 'China'
# 静态方法
@staticmethod
def getCountry():
return People.country
print(People.getCountry())#可以用类对象引用
p = People()
print(p.getCountry())#可以用实例对象引用
China
China
小结
对于类属性和实例属性,如果在类方法中引用某个属性,该属性必定是类属性;而如果在实例方法中引用某个属性(不作更改),并且存在同名的类属性,此时若实例对象有该名称的实例属性,则实例属性会屏蔽类属性,即引用的是实例属性,若实例对象没有该名称的实例属性,则引用的是类属性;如果在实例方法更改某个属性,并且存在同名的类属性,此时若实例对象有该名称的实例属性,则修改的是实例属性,若实例对象没有该名称的实例属性,则会创建一个同名称的实例属性。
想要修改类属性,如果在类外,可以通过类对象修改,如果在类里面,只有在类方法中进行修改。
从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用。
类对象可以访问类方法,不可以访问实例方法;实例对象可以访问类方法,也可以访问实例方法。
特性:@property装饰器
Python内置的@property装饰器,就是负责把类方法变成只读属性调用的。这种把方法变属性,就是特性。既能检查参数,又可以用类似属性这样简单的方式来访问类的变量。
class Student(object):
# 只读,类似于get方法
@property
def Att(self):
return self._score
# 只写,类似于set方法
@Att.setter
def Att(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s = Student()
s.Att = 88 # OK,实际转化为s.set_score(60)
print(s.Att) # OK,实际转化为s.get_score()
s.Att = 999999#只接抛出异常了,异常如下:
# ValueError: score must between 0 ~ 100!
88
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-9-b5dcf8a6f9e9> in <module>()
19 print(s.Att) # OK,实际转化为s.get_score()
20
---> 21 s.Att = 999999#只接抛出异常了,异常如下:
22
23 # ValueError: score must between 0 ~ 100!
<ipython-input-9-b5dcf8a6f9e9> in Att(self, value)
12 raise ValueError('score must be an integer!')
13 if value < 0 or value > 100:
---> 14 raise ValueError('score must between 0 ~ 100!')
15 self._score = value
16
ValueError: score must between 0 ~ 100!
装饰器是在代码运行期间动态增加函数功能的方式,本质上就是一个返回函数的高阶函数。一个完整的decorator写法如下:
import functools
# 不带参数2层嵌套实现
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
@log
def now():
print('2015-3-25')
now()
call now():
2015-3-25
import functools
# 带参数三层嵌套实现
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log("execute")
def now():
print('2015-3-25')
now()
execute now():
2015-3-25
示例:设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间。
import time, functools
def metric(fn):
@functools.wraps(fn)
def wrapper (*args,**kw):
start=time.time()
result=fn(*args,*kw)
end=time.time()
print('%s is executed %s ms' %(fn.__name__, (end-start)*1000))
return result
return wrapper
# 测试
@metric
def fast(x, y):
time.sleep(0.0012)
return x + y;
@metric
def slow(x, y, z):
time.sleep(0.1234)
return x * y * z;
f = fast(11, 22)
s = slow(11, 22, 33)
if f != 33:
print('测试失败!')
elif s != 7986:
print('测试失败!')
fast is executed 1.9998550415039062 ms
slow is executed 124.0072250366211 ms
小结
在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。