python面向对象进阶

时间:2021-09-02 03:30:41

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()函数,三个参数:
    1. 字符串形式的命令
    2. 全局作用域,如果不指定就默认使用globles()
    3. 局部作用域,如果不指定就是用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方法控制类的实例化行为:
    1. 调用对象的时候会触发对象的类的元类内部的__call__()方法,这个call方法与对象是绑定的关系,而且可以接收对象命令参数;
    2. 由于类是其元类的实例化对象,那么在调用类的时候,这个类若指定了元类的话,会触发元类内部的__call__()方法,并且将参数传入call方法
    3. 产生的新对象: 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))