一、类属性与实例属性
类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本。
实例属性(对象属性),它不被所有类对象的实 例对象所共有,在内存中的副本个数取决于对象个数。
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)
turtle1 = Turtle()
print(turtle1.power, turtle1.x, turtle1.y)
100
100 3 5
二、类方法和静态方法
1、类方法
类方法是类对象所拥有的方法,需要用修饰器一般以@classmethod来标识其为类方法,
1). 对于类方法,第一个参数必须是类对象,作为第一个参数 (cls是形参, 可以修改为其它变量名,但最好用’cls’)
2). 能够通过实例对象和类对象去访问。
2、静态方法
静态方法需要用修饰器一般以@staticmethod来标识其为静态方法
1). 静态方法不需要多定义参数
2). 能够通过实例对象和类对象去访问。
类方法和静态方法
import random
class Turtle(object):
def __init__(self):
# x,y:实例属性.
self.x = random.randint(0, 10)
self.y = random.randint(0, 10)
self.power = 100
# 默认情况下, Python解释器会自动将对象传递给类里面定义的方法。
def eat(self):
print("self: ", self)
self.power += 20
@classmethod
def cls_example(cls):
print("cls: ", cls)
@staticmethod
def static_example():
print("静态方法.......")
turtle = Turtle()
turtle.eat()
turtle.cls_example()
turtle.static_example()
# 类方法能够通过实例对象和类对象去访问。
Turtle.cls_example()
# 静态方法能够通过实例对象和类对象去访问。
Turtle.static_example()
self: <__main__.Turtle object at 0x0000024B2D3590B8>
cls: <class '__main__.Turtle'>
静态方法.......
cls: <class '__main__.Turtle'>
静态方法.......
类方法和静态方法的应用
import time
"""
# 系统自带date类的使用
from datetime import date
dateObj = date(2019, 10, 10)
print(dateObj)
print(date.today())
"""
class Date(object):
def __init__(self, year, month, day):
#对象和属性的封装
self.year = year
self.month = month
self.day = day
def __str__(self):
#输出年月日
return "%s-%s-%d" % (self.year, self.month, self.day)
#类方法
@classmethod
def today(cls):
"""
返回当前的日期对象
cls: 类名称Date
:return: Date实例化的对象名称
"""
# 获取当前的时间, 返回的是命名元组的格式
# time.struct_time(tm_year=2019, tm_mon=12, tm_mday=29, tm_hour=16, tm_min=49, tm_sec=32, tm_wday=6, tm_yday=363, tm_isdst=0)
now = time.localtime()
return cls(now.tm_year, now.tm_mon, now.tm_mday)
def is_leap(self):
"""
判断是否为闰年?
一个闰年就是能被4整除但是不能被100整除 或者 year能被400整除.
:return:
"""
return (self.year % 4 == 0 and self.year % 100 != 0) or (self.year % 400 == 0)
if __name__ == '__main__':
# 获取当前日期, today是当前日期对象。
today = Date.today()
# 打印日期对象, 返回字符串'2019-12-29', 因为有__str__魔术方法。
print(today)
# 判断是否为闰年?
print("今年是否为闰年? ", today.is_leap())
# dateObj = Date(2019, 10, 10)
# print(dateObj)
2020-1-2
今年是否为闰年? True
三、property类属性
什么是property属性?
一种用起来像是使用的实例属性一样的特殊属性,可以对应于类的某个方法。
property属性的定义和调用要注意一下几点:
- 定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数
- 调用时,无需括号
property属性的有两种方式:
• 装饰器 即:在方法上应用装饰器
• 类属性 即:在类中定义值为property对象的类属性
注意:
• 经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
• 新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
property类属性的应用
from datetime import date
class Date(object):
def __init__(self, year, month, day):
self.__year = year
self.__month = month
self.__day = day
# 功能一: 将类方法转换成类属性
# 应用场景: 某个属性只能访问不能修改时使用。
@property
def year(self):
return self.__year
@property
def month(self):
return self.__month
@property
def day(self):
return self.__day
def __str__(self):
return "%s-%s-%d" % (self.__year, self.__month, self.__day)
if __name__ == '__main__':
dateObj = Date(2019, 12, 12)
print(dateObj.day)
12
案例:preproty商品应用
需求:分页实现
• 根据用户请求的当前页和总数据条数计算出 start 和 end
• 根据start 和 end 去数据库中请求数据
• 是否有上一页has_prev、下一页has_next
• 上一页prev、下一页next
• 总页数pages, 数据总条数total、当前页信息items
class Pagintor(object):
"""实现商品分页的类"""
def __init__(self, objects_list, page=1, per_page=5):
"""
:param objects_list: 商品列表
:param page: 当前需要显示的页码信息
:param per_page: 每页显示的数据个数
"""
self.objects_list = objects_list
self.page = page
self.per_page = per_page
# 应用场景二: 某一个属性不能直接返回, 需要计算的, 可以通过property属性实现
@property
def start(self):
return (self.page - 1) * self.per_page
@property
def end(self):
return self.page * self.per_page
@property
def total(self):
"""
数据总条数total
:return:
"""
return len(self.objects_list)
@property
def pages(self):
"""
总页数pages
if 总商品数量%每页显示数量==0: 刚好当前页显示满
else: 好友多与的部分, 在计算的结果上面加1
self.total = 5 pages=1
self.total = 6 pages=2
:return:
"""
result = self.total // self.per_page
if self.total % self.per_page == 0:
return result
else:
return result + 1
@property
def has_next(self):
"""判断是否存在下一页"""
return True if 0 < self.page + 1 <= self.pages else False
@property
def next(self):
"""显示下一页信息"""
next_page = self.page + 1
next_start = self.page * self.per_page
next_end = next_page * self.per_page
return self.objects_list[next_start:next_end]
@property
def has_prev(self):
"""判断是否存在上一页"""
return True if 0 < self.page - 1 <= self.pages else False
@property
def prev(self):
"""显示上一页信息"""
prev_page = self.page - 1
prev_start = (prev_page - 1) * self.per_page
prev_end = prev_page * self.per_page
return self.objects_list[prev_start:prev_end]
@property
def items(self):
"""
当前页信息items
:return:
"""
return self.objects_list[self.start:self.end]
if __name__ == '__main__':
# 应用场景二: 某一个属性不能直接返回, 需要计算的, 可以通过property属性实现
goods = ["电脑" + str(i) for i in range(100)]
# 需求: 显示第三页时, 开始的索引是? 结束的索引为多少?
pagintor = Pagintor(goods, page=2, per_page=5)
print("商品总数为:",pagintor.total)
print("商品总页数为:",pagintor.pages)
print("当前页的商品信息为: ", goods[pagintor.start:pagintor.end])
print("是否有上一页?", pagintor.has_prev)
print("上一页的商品信息为:",pagintor.prev)
print("是否具有下一页? ",pagintor.has_next)
print("下一页的商品信息为: ",pagintor.next)
商品总数为: 100
商品总页数为: 20
当前页的商品信息为: ['电脑5', '电脑6', '电脑7', '电脑8', '电脑9']
是否有上一页? True
上一页的商品信息为: ['电脑0', '电脑1', '电脑2', '电脑3', '电脑4']
是否具有下一页? True
下一页的商品信息为: ['电脑10', '电脑11', '电脑12', '电脑13', '电脑14']
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("年龄属性删除......")
# 类属性 即:在类中定义值为property对象的类属性
age = property(fget=get_age, fset=set_age, fdel=del_age)
if __name__ == '__main__':
p1 = Person("张三", 30)
print(p1.age)
p1.age = 31
print(p1.age)
del p1.age
30
31
年龄属性删除......
property通过装饰器实现类属性
新式类中的属性有三种访问方式,并分别对应了三个被@property、@方 法名.setter、@方法名.deleter修饰的方法
class Person(object):
def __init__(self, name, age, score):
self.name = name
self.__age = age # 私有属性
self.__score = score
@property
def score(self):
return self.__score
@score.setter
def score(self, score):
self.__score = score
@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 # 删除age属性时执行的内容
def age(self):
print("年龄属性删除......")
if __name__ == '__main__':
p1 = Person("张三", 30, 100)
print(p1.age) # 获取年龄(), 执行@property def age(self):
p1.age = 31 # 设置年龄, age=31
print(p1.age)
del p1.age # 删除年龄属性
30
31
年龄属性删除......
四、单例模式
单例模式简单来说就是一个类只能创建一个对象,不能创建多个对象。例如,一个系统中可以存在多个打印任务, 但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有 一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制 对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象, 浪费内存资源。
装饰器实现单例模式
from functools import wraps
def singleton(cls):
"""
实现单例模式的装饰器
思路: 当实例化对象时, 判断该类是否实例化过对象。
- 如果是, 返回之前实例化的对象。
- 如果不是, 实例化第一个对象, 并将实例化后的对象存储起来(缓存)。
"""
instances = {} # {'Person': obj}
@wraps(cls)
def wrapper(*args, **kwargs):
name = cls.__name__
if instances.get(name):
# 直接返回缓存中的对象
return instances.get(name)
else:
# 第一次实例化对象
obj = cls(*args, **kwargs)
# 类名作为key值, 对象作为value值, 存储到instances字典中.
instances[name] = obj
return obj
return wrapper
@singleton
class Person(object):
pass
if __name__ == '__main__':
p1 = Person()
p2 = Person()
# 面试题目: ==和is有什么区别?
#==运算操作符,判断value值是否相同;is是判断同一性,id和value是否都相同
print("单例模式是否成功?", p1 is p2)
单例模式是否成功? True
new方法实现单例模式
from datetime import date
class Person(object):
def __new__(cls, *args, **kwargs):
print("判断当前类是否拥有instance属性?", hasattr(cls, 'instance'))
if not hasattr(cls, 'instance'):
cls.instance = super(Person, cls).__new__(cls)
return cls.instance
def __init__(self, name):
self.name = name
p1 = Person("张三")
p2 = Person("张xxx")
print("单例模式是否成功? ", p1 is p2)
判断当前类是否拥有instance属性? False
判断当前类是否拥有instance属性? True
单例模式是否成功? True