面相对象高级特性
1.类属性与实例属性
类属性:类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本。
实例属性:前面文章提到的属性都是实例属性(对象属性),它不被所有类对象的实例对象所共有,在内存中的副本个数取决于对象的个数。
下面我们通过一个简单例子来看一下类属性与实例属性的不同:
class People(object):
# 类属性, 在内存中只存一份;
country = 'China'
__gender = 'male'
def __init__(self, name, age, money):
# self.name, self.age, self.money: 实例属性(有多少个对象, 就有多少份属性;)
self.name = name
self.age = age
self.money = money
# 实例化三个对象, 有三份实例属性;
p1 = People("westos1", 10, 1000)
print(p1.name)
print(p1.age)
print(p1.money, id(p1.money))
p2 = People("westos2", 15, 1030)
print(p2.money, id(p2.money))
p3 = People("westos3", 18, 1500)
print(p3.money, id(p3.money))
# 调用p1, p2, p3的属性country: 当对象没有country属性时, 调用类的属性country;
print(p1.country, p2.country, p3.country)
# 设置p2对象的属性country为‘us’, 并不会修改类的属性;
p2.country = 'US'
# 调用p1, p2, p3的属性country: 当对象没有country属性时, 调用类的属性country; 当对象有country‘属性时, 调用拥有的country'属性;
print(p1.country, p2.country, p3.country)
输出结果为:
我们可以总结出类属性和实例属性的不同点:
不同点
|
类属性 |
实例属性 |
定义 |
直接在类里面定义 |
属性要和对象绑定在一起 |
所占用内存不同 | 只存一份, 跟对象的个数无关 |
有多少个对象, 存储多少份 |
调用时方法不同 | 类名.属性名, 对象名.属性名 |
对象名.属性名 |
2. 静态方法与类方法
静态方法:使用装饰器@staticmethod标识。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法。
静态方法能够通过实例对象和类对象去调用。
类方法:是类对象所拥有的方法,使用装饰器@classmethod标识。第一个参数必须是当前类,该参数名一般约定为“cls” class的缩写, 通过它来传递类的属性和方法(不能传实例的属性和方法)。
类方法也能够通过实例对象和类对象去调用。
下面通过一个实例看一下怎样应用静态方法与类方法:
class Date(object):
# 魔术方法(构造方法)
def __init__(self, year=2019, month=6, day=18):
self.year = year
self.month = month
self.day = day
# 实例方法
def echo(self):
print("%s-%s-%s" % (self.year, self.month, self.day))
# 类方法: 默认传递的第一个参数是类名
@classmethod
def as_string(cls, str):
"""
# # 将字符串的时间处理, 并执行echo方法
"""
# print(cls) # <class '__main__.Date'>
# 将字符串的时间处理, 并执行echo方法
month, day, year = str.split('/')
d1 = cls(year, month, day)
return d1
@staticmethod
def is_vaild(str):
# str = '10/1/2019'
month, day, year = map(int, str.split('/'))
return year > 0 and 0 < month <= 12 and 0 < day <= 31
d = Date()
# 运行类的实例方法
d.echo()
str = '10/1/2019'
d1 = Date.as_string(str)
d1.echo()
print(Date.is_vaild('13/13/2019'))
输出结果为:
这里再提一个python的内置装饰器property:
Python内置的@property装饰器就是负责把一个方法变成属性调用的,调用时直接对象名.属性名。@property广泛应用在类的定义中,可以让调用者写出简短的代码, 同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性。
另外,@property本身又创建了另一个装饰器@state.setter,负责把一个 setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作。
@property使用比较简单,下面看一个例子看以下怎么使用它:
import time
class Date(object):
def __init__(self, year=None, month=None, day=None):
self.__year = year
self.__month = month
self.__day = day
@property # 类属性, 获取属性名时简洁: 对象名.属性名
def month(self):
if 0 < self.__month <= 12:
return self.__month
@month.setter # 设置属性装饰器, 当设置属性时简洁: 对象名.属性名=新的值
def month(self, month):
if 0 < self.__month <= 12:
self.__month = month
return True
@month.deleter # 删除属性装饰器, 当删除属性时简洁: del 对象名.属性名
def month(self):
del self.__month
d = Date(2019, 10, 10)
print(d.month)
d.month = 12
print("正在修改月份....")
print(d.month)
del d.month
print("正在删除月份....")
print(d.month)
在这里还用到一个删除属性的装饰器,可以把属性删除掉。验证程序的时候,后面删掉了属性,之后又打印删掉的属性,所以就会有报错,结果如下:
3.单例模式
什么是单例模式呢?顾名思义,就是一个类只能构建一个对象的设计模式。
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
装饰器(decorator)可以动态地修改一个类或函数的功能。这里,我们也可以使用装饰器来 装饰某个类,使其只能生成一个实例,代码如下:
def singleton(cls):
"""
用来装饰类的装饰器: 实例化类时, 只实例化一个对象;
"""
# 初始化一个字典instances, key: 类名 , value: 类实例化的对象
instances = {}
def wrapper(*args, **kwargs):
"""
如果类在缓存instances存在时, 直接返回类对应的对象;
如果类不在缓存instances存在时, 先实例化对象, 将类名和对象名存储到缓存中instances;
"""
if cls in instances:
return instances[cls]
else:
# instances[cls] = cls(*args, **kwargs)
obj = cls(*args, **kwargs)
instances[cls] = obj
return obj
return wrapper
@singleton # @single =====> Student = singleton(Student)
class Student(object):
pass
# 如果类有被装饰器sigleton装饰, 返回的s1和s2时两个相同的对象(内存地址相同);
s1 = Student()
s2 = Student()
print(s1, s2)
我们可以通过输出的结果看到,返回的s1和s2的内存地址相同: