面向对象的高级特性
1. 类属性与实例属性
类属性:类对象所拥有的属性,它被所有类的实例对象所共有,1) 在内存中只存在一个副本
;2) 可通过类名访问也可通过对象名访问
。
实例属性:即对象属性,它不被所有类对象的实例对象所共有,1) 在内存中的副本个数取决于对象个数
;2) 只能通过对象名访问
。
代码示例:
import random
class Turtle(object):
# power是类属性。
power = 100
def __init__(self):
# x,y: 实例属性.
self.x = random.randint(0, 10)
self.y = random.randint(0, 10)
# 1). 类属性不管有多少个对象, 都只存储一份。 实例属性存储的个数取决于实例的个数.
# 2). 作用域不同:
# 类属性: 通过类名/对象名来访问
# 实例属性: 只能通过对象名来访问。
print(Turtle.power)
my_turtle = Turtle()
print(my_turtle.power, my_turtle.x, my_turtle.y)
2. 类方法和静态方法
类方法:类对象所拥有的方法,需要用修饰器@classmethod
来标识其为类方法。
- 对于类方法,第一个参数必须是类对象,一般写作cls (cls是形参, 可以修改为其它变量名,但最好用 ‘cls’ )
- 能够通过实例对象和类对象去访问。
静态方法:需要用修饰器@staticmethod
来标识其为静态方法,
- 静态方法不需要多定义参数
- 能够通过实例对象和类对象去访问。
类方法和静态方法的应用之对date 模块的再次封装 (类似于datetime模块),代码示例:
import random
class Turtle(object):
def __init__(self):
# x, y: 实例属性.
self.x = random.randint(0, 10)
self.y = random.randint(0, 10)
# 默认情况下, Python解释器会自动将实例对象传递给类里面定义的方法。
def eat(self):
print("self: ", self)
# 类方法, Python解释器会自动将类对象传递给类方法。
@classmethod
def cls_example(cls):
print("类方法,cls: ", cls)
# 静态方法,不需要多定义参数
@staticmethod
def static_example():
print("静态方法.......")
turtle = Turtle()
turtle.eat()
print("分界线".center(30, "-"))
# 类方法能够通过实例对象和类对象去访问。
turtle.cls_example()
Turtle.cls_example()
print("分界线".center(30, "-"))
# 静态方法能够通过实例对象和类对象去访问。
turtle.static_example()
Turtle.static_example()
执行结果:
3. property类属性
property类属性:在使用实例方法时能够将方法的调用变为像实例属性的调用一样的特殊属性,即将类方法转换为类属性。
property属性的定义和调用要需要注意:
- 定义时,在实例方法的基础上添加
@property
装饰器;并且实例方法仅有一个self参数 - 调用时,无需括号的调用 (即像调用属性一般调用方法即可)
3.1 property属性应用场景
- 场景一:某个属性只能访问不能修改时
- 场景二:某个属性不能直接返回,需要计算、异常处理等函数操作的
具体场景有:
类属性应用需求: 对于京东商城中显示电脑主机的列表页面,每次请求不可能把数据库中的所有内容都显示到页面上,而是通过分页的功能局部显示,所以在向数据库中请求数据时就要显示的指定获取从第m条到第n条的所有数据 这个分页的功能包括:
- 根据用户请求的当前页和总数据条数计算出 m 和 n
- 根据m 和 n 去数据库中请求数据
代码如下:
# 应用场景二的情境
class Pagintor(object):
def __init__(self, goods, page=1, per_page=5):
self.goods = goods
self.page = page
self.per_page = per_page
@property
def start(self):
return (self.page - 1) * 5
@property
def end(self):
return self.page * 5
@property
def current_item(self):
return self.goods[self.start: self.end]
@property
def total(self):
return len(self.goods)
@property
def pages(self):
zheng_shu, yu_shu = divmod(len(self.goods), self.per_page)
result = zheng_shu if yu_shu == 0 else zheng_shu + 1
return result
@property
def hasNext(self):
return True if 0 < self.page+1 <= self.pages else False
@property
def hasPrevious(self):
# return True if self.start >= self.per_page else False
return True if 0 < self.page-1 <= self.pages else False
@property
def previous(self):
if self.hasPrevious:
return goods[(self.page-2)*5 : (self.page-1)*5]
else:
return "已是第一页"
@property
def next(self):
if self.hasNext:
return goods[self.page * 5: (self.page+1) * 5]
else:
return "已是最后一页"
"""
需求:每页显示 per_page=5 条数据,获取第page页的开始索引和结束索引
page start end
1 0 5
2 5 10
3 10 15
star = (page - 1) * per_page
end = page * per_page
"""
if __name__ == '__main__':
goods = ["电脑" + str(i) for i in range(15)]
pagintor = Pagintor(goods, page=2)
# 利用@property装饰的方法调用方式不再是 “对象名.函数名()”,而是
# 而是 “对象名.函数名” 像属性般的调用方式
print("第2页的商品信息为:", pagintor.current_item)
print("是否有上一页:", pagintor.hasPrevious)
print("是否有下一页:", pagintor.hasNext)
print("下一页商品信息:", pagintor.next)
print("上一页商品信息:", pagintor.previous)
执行结果:
3.2 property属性实现方式
- 装饰器 即:在方法上应用装饰器,代码示例如上述“分页器”所示
- 类属性 即:在类中定义值一个为property对象的类属性 (或称作函数式实现法),代码示例:
# 场景一和场景二的情境
class Person(object):
def __init__(self, name, age):
self.name = name
self.__age = age
@property
def is_age_vaild(self):
return 0 < self.__age <= 150
def get_age(self):
if self.is_age_vaild:
return self.__age
else:
raise Exception("年龄不合法")
def set_age(self, age):
if self.is_age_vaild:
self.__age = age
else:
raise Exception("年龄不合法")
def del_age(self):
print("年龄属性已删除...")
# age 是Person类的类属性 ,也是(被定义为)property的实例化对象
age = property(fget=get_age, fset=set_age, fdel=del_age)
if __name__ == '__main__':
p1 = Person("xiaohong", 100)
# p1.age --> 自动调用get_age方法
print(p1.age)
# 对age修改 --> 自动调用set_age方法
p1.age = 101
print(p1.age)
# 对age删除 --> 自动调用del_age方法
del p1.age
- 对于新式类中的属性,属性的三种访问方式(查、改、删),分别对应三个被
@property
、@方法名.setter
、@方法名.deleter
修饰的方法 (经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法),代码示例如下:
class Person(object):
def __init__(self, name, age):
self.name = name
self.__age = age
@property
def is_age_vaild(self):
return 0 < self.__age <= 150
@property # 获取age 属性时执行的内容
def age(self):
if self.is_age_vaild:
return self.__age
else:
raise Exception("年龄不合法")
@age.setter # 设置age 属性时执行的内容
def age(self, age):
if self.is_age_vaild:
self.__age = age
else:
raise Exception("年龄不合法")
@age.deleter
def age(self): # 删除age 属性时执行的内容
print("年龄属性已删除...")
if __name__ == '__main__':
p1 = Person("xiaohong", 100)
# p1.age --> @property ...
print(p1.age)
# 对age修改 --> @age.setter ...
p1.age = 101
print(p1.age)
# 对age删除 --> @age.deleter ...
del p1.age
4. 单例模式
单例模式:一个类只能创建一个实例的设计模式
。
应用场景:对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
4.1 单例模式的装饰器实现方法
装饰器(decorator)可以动态地修改一个类或函数的功能。–> 使用装饰器来装饰某个类,使其只能生成一个实例,代码如下:
from functools import wraps
def singleton(cls):
instance = {} # eg:{cls: obj}
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instance:
instance[cls] = cls(*args, **kwargs)
return instance[cls]
return wrapper
@singleton
class MyClass(object):
pass
if __name__ == '__main__':
cls1 = MyClass()
cls2 = MyClass()
print("单例模式是否成功:", cls1 is cls2)
4.2 单例模式__new__的实现方法
class Person(object):
_instance = None
def __new__(cls, *args, **kwargs):
"""new 方法在实例化对象之“前”调用,返回对象本身"""
if not cls._instance:
cls._instance = object.__new__(cls)
return cls._instance
if __name__ == '__main__':
p1 = Person()
p2 = Person()
print("单例模式是否成功:", p1 is p2)
# *******__new__实现方法【改进版】*******
class Person(object):
def __new__(cls, *args, **kwargs):
"""new 方法在实例化对象之“前”调用,返回对象本身"""
if not hasattr(cls, "instance"):
cls.instance = object.__new__(cls)
return cls.instance
if __name__ == '__main__':
p1 = Person()
p2 = Person()
print("单例模式是否成功:", p1 is p2)
4.3 单例模式的重写元类的实现方法
如果对元类的理解还不够深刻,那么,对于单例模式的实现方式并不推荐重写元类的方法,因为99%情况不需要自己自定义元类。但是下述代码仍对深刻理解对象、类、元类、实例化、魔术方法等有一定帮助。
class Singleton(type):
"""自定义元类实现单例模式"""
cache = {}
def __call__(cls):
if cls not in cls.cache:
cls.cache[cls] = super(Singleton, cls).__call__()
return cls.cache[cls]
class Person(object, metaclass=Singleton): # metaclass一个指定元类的参数
pass
if __name__ == '__main__':
# 注意:Person是Singleton元类实例化出的对象, Person()就是对象(), 执行Singleton.__call__魔术方法.
p1 = Person()
p2 = Person()
print("单例模式是否成功:", p1 is p2)