面向对象高级特性
类属性与实例属性
- 类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本。
- 在前面的例子中我们接触到的就是实例属性(对象属性),它不被所有类对象的实例对象所共有,在内存中的副本个数取决于对象个数。
"""
不同点整理:
类属性 对象属性
1. 定义 直接定义在类里面country='xxx' 属性和对象绑定在一起: self.name='xxxx'
2. 占用内存不一样 只存一份, 跟对象的个数无关 有多少个对象, 存储多少份;
3. 调用的时候不一样 类名.属性名, 对象名.属性名 对象名.属性名
"""
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, 1000000000)
print(p1.name)
print(p1.age, id(p1.age))
print(p1.money, id(p1.money))
p2 = People("westos2", 10, 1000000000)
print(p2.money, id(p2.money))
p3 = People("westos3", 10, 1000000000)
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)
# 类属性名如果前面添加双下划线, 那么他是私有属性, 类的外部时不能访问的;
print(p1._People__gender)
print(People.country)
实例方法,静态方法与类方法
- 实例方法是实例对象所拥有的方法
1). 对于实例方法,第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法;
2). 只能由实例对象调用。 - 类方法是类对象所拥有的方法,需要用修饰器一般以@classmethod来标识其为类方法
1). 对于类方法,第一个参数必须是类对象,作为第一个参数
(cls是形参, 可以修改为其它变量名,但最好用’cls’)
2). 能够通过实例对象和类对象去访问。 - 静态方法需要用修饰器一般以@staticmethod来标识其为静态方法
1). 静态方法不需要多定义参数
2). 能够通过实例对象和类对象去访问。 - 把方法变成一个属性名来使用(不用加())
需要使用@property装饰器来实现,使用后会同时出现@被装饰的函数名.setter,@被装饰得函数名.deleter两个新的装饰器,可以用来给该属性直接赋值和删除属性
class Date(object):
y = 2019
m = 6
d = 16
def __init__(self, y1=2000, m1=5, d1=10):
self.y1 = y1
self.m1 = m1
self.d1 = d1
def echo(self):
print('%s-%s-%s' % (self.y1, self.m1, self.d1))
@classmethod
def change(cls, str):
y2, m2, d2 = str.split('/')
date = cls(y2, m2, d2)
return date
@staticmethod
def isVaild(str):
y, m, d = map(int, str.split('/'))
return y > 0 and 0 < m < 12 and 0 < d < 31
@property # 把方法变成一个属性名来使用,不加()
def year(self):
return self.y1
@year.setter
def year(self, year):
self.y1 = year
@year.deleter
def year(self):
del self.y1
str = '1999/1/1'
print('=========实例方法,属性=======')
date = Date()
date.echo()
print('=========类方法=======')
date1 = Date.change(str)
date1.echo()
print('=========类属性=======')
print(date.y, date.m, date.d)
print('=========静态方法=======')
print(Date.isVaild(str))
print('=========方法转属性装饰器=======')
print(date.year)
date.year = 2111
print(date.year)
del date.year
#print(date.year)
单例模式
对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要
方法一:使用装饰器装饰需要单例的类对象(新的实例对象不会覆盖旧实例对象)
def singleton(cls):
instances = {}
def wrapper(*args, **kwargs):
if cls in instances:
return instances[cls]
else:
obj = cls(*args, **kwargs)
instances[cls] = obj
return obj
return wrapper
@singleton
class S(object):
pass
s1 = S()
s2 = S()
print(s1, s2) # 地址一样,只会创建一个
解析:
s1存在,直接把s1对象赋值给s2,所以不会覆盖
方法二:重写__new__方法实现单例模式(新的实例对象会覆盖旧实例对象)
# 实例化一个单例
class Singleton(object):
__instance = None
def __new__(cls, *args, **kwargs):
# 如果类数字能够__instance没有或者没有赋值
# 那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时
# 能够知道之前已经创建过对象了,这样就保证了只有1个对象
if not cls.__instance:
cls.__instance = super().__new__(cls)
return cls.__instance
return cls.__instance
def __init__(self, age, name):
self.age = age
self.name = name
a = Singleton(18, "dongGe")
b = Singleton(8, "Ge")
print(a)
print(b)
print(a.age, b.age)
print(a.name, b.name)
解析:
a存在,则b会接收到a对象的内存地址,在该地址上重新创建b对象,所以覆盖a对象