python面向对象进阶
常用内置方法: items三剑客,str方法, del方法
元类的基本知识:exec(),一切皆为对象
property特性的进阶应用
元类init方法
call方法
运用元类实现单例模式的实现
item系列方法
- 三剑客:
__getitem__
,__setitem__
,__delitem__
- python内置的item系列方法可以将类的操作变成类似与字典一样操作
class People:
def __init__(self, name, age, sex):
self.age = age
self.name = name
self.sex = sex
def __getitem__(self, item):
cls_name = self.__class__.__name__
print(self.__dict__.get(item, ''))
def __delitem__(self, key):
print(key)
del self.name
print('delete------')
def `__setitem__`(self, key, value):
print(key, value)
self.__dict__[key] = value
p1 = People('jack', 28, 'male')
p1['name'] = 'bob' # 自动调用__setitem__()方法
p1['name'] # 会自动调用 __getitem__() 方法
del p1['name'] # 会自动调用 __delitem__() 方法
类的__str__
方法
-
print
类的实例化对象的一般刚返回的是对象的在内存的地址,<__main__.People object at 0x7f0da231a6d8>
, 对用户没有帮助。 - 但是如果类的内部定义了
__str__()
这个方法,那么打印对象的时候,会直接调用__str__()
方法, 方法返回的结果必须为字符串,那么打印对象的结果就是return返回的字符串内容。
class People:
def __init__(self, name, age, sex):
self.age = age
self.name = name
self.sex = sex
def __str__(self):
return '姓名<%s>' % self.name
p1 = People('lynnfang', 28, 'male')
print(p1)
property特性的进阶应用
class People:
def __init__(self, name, age, sex):
self.__age = age
self.__name = name
self.__sex = sex
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('Must be a string')
self.__name = value
@name.deleter
def name(self):
raise TypeError("Can not delete a name!")
p = People('jack', 29, '男')
p.name = 'asa'
print(p.name)
del p.name
类的__del__
方法
-
如果在类内部定义了del方法,那么在实例化对象被删除的之前会调用此方法
如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了
__del__
方法.
class People:
def __init__(self, name, age, sex):
self.age = age
self.name = name
self.sex = sex
def __str__(self):
return '姓名<%s>' % self.name
def __del__(self):
print('delete-----')
p1 = People('lynnfang', 28, 'male')
print('end----')
元类基本知识
- exec()函数,三个参数:
- 字符串形式的命令
- 全局作用域,如果不指定就默认使用globles()
- 局部作用域,如果不指定就是用locals()
- 体内声明全局变量后,那么就可以直接修改x,m的值,即全局命名空间,m为局部变量,写入局部命名空间
global_ = {'x':1, 'y':2}
local_ = {}
body = '''
global x,m # 声明全局变量,会自动加进全局命名空间,且对原空间内的变量进行修改
x = 3
y = 6 # 加进局部命名空间
m = 10 '''
exec(body, global_, local_)
print('global_: <%s>\nlocal_: <%s>' % (global_, local_))
- 在python的世界里,一切皆为对象:
- 可以被引用:
x = obj
- 可以当做函数的参数传入
- 可以作为函数的返回值
- 可以当做容器类型的元素
- 可以被引用:
默认产生类的类称之为元类,所有用class定义的类,它们的元类都是type:
<class 'type'>
- 利用元类创建类
- 指定类名
- 继承类,以元祖形式
- 类体代码
利用
type( )
定义类实例, 三个参数: 类名,基类,和类的名称空间dict三个参数,返回一个类。
class_name = "Account" # 类名
class_bases = (object,) # 类的父类
# 类体
class_body = '''
bank='CBC'
def __init__(self, username, password, balance):
self.__Username = username
self.__Password = password
self.__balance = balance
def check_account(self):
print('账户余额: %s' % self.__balance)
'''
class_dict = {}
exec(class_body, globals(), class_dict)
Account1 = type(class_name, class_bases, class_dict)
自定义元类控制类的行为
类本身就是一个对象,在调用类的时候会触发这个类的元类的
__init__()
方法- 且将类名, 类继承的父类tuple, 类的命名空间传入到元类的
__init__()
方法内部 一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类
class Animal(type):
def __init__(self, class_name, class_bases, class_dict):
# 只能控制类的一些数据属性
if not str(class_name).istitle():
raise TypeError('类名手写字母必须大写')
if not '__doc__' in class_dict or not class_dict.get('__doc__', '').strip():
raise TypeError('必须对类作相关说明')
class Dog(object,metaclass=Animal):
"""狗类"""
def __init__(self, name, age, gender):
self.Name = name
self.Age = age
self.Gender = gender
__call__
方法
- 元类利用call方法控制类的实例化行为:
- 调用对象的时候会触发对象的类的元类内部的
__call__()
方法,这个call方法与对象是绑定的关系,而且可以接收对象命令参数; - 由于类是其元类的实例化对象,那么在调用类的时候,这个类若指定了元类的话,会触发元类内部的
__call__()
方法,并且将参数传入call方法 - 产生的新对象:
object.__new__()
- 调用对象的时候会触发对象的类的元类内部的
class Animal(type):
class_num = 0
# 仅在生成元类的对象类的时候调用,并传入类的三个属性,所以只能控制类的一些数据属性
def __init__(self, class_name, class_bases, class_dic):
print('in __init__')
if not str(class_name).istitle():
raise TypeError('类名首写字母必须大写!')
if '__doc__' not in class_dic or not class_dic.get('__doc__', '').strip():
raise TypeError('必须对类作相关说明!')
super().__init__(class_name, class_bases, class_dic)
# 在元类的对象类进行实例化的时候调用
def __call__(self, *args, **kwargs):
# 1.建立空对象
obj = object.__new__(self)
# 2.调用类的初始化函数对空对象进行初始化, 传入参数
self.__init__(obj, *args, **kwargs)
return obj
class People(metaclass=Animal):
"""人类"""
def __init__(self, name, age):
"""由于是继承自元类,本身就是对象,不会自动调用此函数,需要在元类的call方法中调用
"""
print('in people init')
self.name = name
self.age = age
def run(self):
print('%s is running' % self.name)
d = People('frank', 29)
print(d.__dict__)
运用元类实现单例模式的实现
- 比如数据库对象,实例化时参数都一样,就没必要重复产生对象,浪费内存
class Mysql:
__instance=None
def __init__(self,host='127.0.0.1',port='3306'):
self.host=host
self.port=port
@classmethod
def singleton(cls,*args,**kwargs):
if not cls.__instance:
cls.__instance=cls(*args,**kwargs) # 实例化对象并返回
return cls.__instance
obj1=Mysql()
obj2=Mysql()
print(id(obj1))
print(id(obj2))
print('============')
obj1 = Mysql.singleton()
obj2 = Mysql.singleton()
print(obj1==obj2)
print(obj1.__dict__)
print(obj2.__dict__)
class SchoolMeta(type):
def __init__(self, name, bases, dict):
"""仅在定义school类的时候调用一次"""
super().__init__(name, bases, dict)
self.__instance = None
self.__instance_num = 0
def __call__(self, *args, **kwargs):
"""school类实例化的时候调用"""
if not self.__instance:
# 1.第一次调用, 生成空对象
self.__instance = object.__new__(self)
# 2.调用类的init函数初始化空对象
self.__init__(self.__instance, *args, **kwargs)
return self.__instance
class School(metaclass=SchoolMeta):
"""school类,是SchoolMetal这个元类的对象"""
def __init__(self, name='希望小学', address='英山'):
self.name = name
self.address = address
s = School()
s1 = School()
print(id(s1)==id(s))