11.面向对象3

封装

封装的介绍
1.什么是封装 *****
装:往容器/名称空间里存入名字
封:代表将存放于名称空间中的名字给藏起来,这种隐藏对外不对内
2.为何要封装
封数据属性:???
封函数属性:???
3.如何封装
在类内定义的属性前加__开头

封装属性的意义
1.封装数据属性:将数据属性隐藏起来,类的外面就无法直接操作属性,怎么间接使用?
2.需要类内开辟一个接口来外部的使用可以间接地操作属性,可以在
3.从而严格控制使用对属性的操作

class People:
	def __init__(self,name,age):
    	self.__name = name
    	self.__age = age
	def tell_info(self):
    	print('name:%s age:%s'%(self.__name,self.__age))
	def set_info(self,name,age):
    	if type(name) is not str:
        	print('名字必须为字符串')
        	return
    	if type(age) is not int:
        	print('年龄必须为数字')
        	return
   		self.__name = name
    	self.__age = age

obj=People('大海',18)
#严格的控制查看属性
#obj.__name
obj.tell_info()	# 可以通过接口间接的访问到属性
# 严格的控制修改属性
# obj.__name = '夏洛'是修改不了的
#_People__name
# print(obj.__dict__)
obj.set_info('夏洛',16)	#通过接口间接访问属性
# print(obj.__dict__)
obj.tell_info()

封装函数的意义
封装函数属性:隔离复杂度 # 间接的访问一些不需要使用者知道的方法

class ATM:
	# 这些功能使用者没必要知道
	# 也就是说把类的内部没必要让使用者知道的功能封装起来
	def __card(self):
    	print('插卡')
    def __auth(self):
        print('用户认证')
    def __input(self):
        print('输入取款金额')
    def __print_bill(self):
        print('打印账单')
    def __take_money(self):
        print('取钱')
# 使用者只需要知道取款功能
# 在类内部开一个统一的接口按步骤依次调用就可以了
def withdraw(self):
    self.__card()
    self.__auth()
    self.__input()
    self.__print_bill()
    self.__take_money()
a = ATM()
a.withdraw()

@property的使用

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
例:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)
成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
体质指数(BMI)=体重(kg)÷身高^2(m)

class People:
    def __init__(self,name,weight,height):
        self.name = name
        self.weight = weight
        self.height = height
    @property
    def bmi(self):
        return self.weight / (self.height**2)
obj=People('大海',70,1.75)
bmi=obj.bmi
print(bmi)

反射

反射:通过字符串来反射/映射到对象/类的属性上
创建类

class People:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def run(self):
        print('%s is running'%self.name)
obj=People('dahai',18)
name = '大海'
print(obj.__dict__)

所以用户输入的是一个字符串的形式,我们怎样把它变成属性名,就用到了反射
判断对象是否有属性,有就返回True,没有就算False

print(hasattr(obj,'name'))
print(hasattr(obj,'aaa'))

获取对象属性

print(getattr(obj,'name'))
# 第三个参数找不到返回None
print(getattr(obj,'xxx',None))

修改属性

setattr(obj,'name','dahai2')

不存在就增加

setattr(obj,'yyy','www')
print(getattr(obj,'yyy',None))

删除对象属性值

delattr(obj,'name')
print(obj.__dict__)

单列模式

单列模式的介绍
1.什么是单例模式 *****
单:同一个
例: 实例/对象
模式:一个模板或者一个方法
单例模式:基于某种方法实例化多次得到实例/对象是同一个
2.为何用单例模式
当实例化多次得到的对象中(存放的属性)都(一样)的情况,应该将多个对象指向同一个内存,即同一个实例
3.如何用

绑定类方法的单例模式

class Mysql:
    # 封装一下 对内不对外
    __instance = None
    def __init__(self,ip,port):
        self.ip =ip
        self.port = port
    @classmethod
    def from_conf(cls):
        # print('类方法绑定')
        if cls.__instance is None:
        	# 完成Mysql对象的初始化 Mysql('127.0.0.1',1234)
        	# cls.__instance 就是Mysql的对象
            cls.__instance = cls('127.0.0.1',1234) 
            
        # 拿到的是Mysql('127.0.0.1',1234)实例化 
        return cls.__instance
        
obj1=Mysql.from_conf()
obj2=Mysql.from_conf()
obj3=Mysql.from_conf()
print(id(obj1))
print(id(obj2))
print(id(obj3))
print(obj1.ip)
print(obj2.ip)
print(obj3.ip)
print(obj1.port)
print(obj2.port)
print(obj3.port)
# 不单例
obj4 = Mysql('127.0.0.1',5678)
print(id(obj4))
print(obj4.port)

元类

在python中统一了类与类型的概念
什么是元类:
源自一句话:在python中,一切皆对象,而对象都是由类实例化得到的
关系:
1.调用元类---->自定义的类
2.调用自定义的类---->自定义的对象

type元类创建自定义类
class关键字创建自定义类的底层的工作原理,分为四步
1.先拿到类名:‘Teacher’
2.再拿到类的基类们:(object,)
3.然后拿到类的名称空间???(执行类体代码,将产生的名字放到类的名称空间也就是一个字典里,补充exec)
4.调用元类实例化得到自定义的类:Teacher=type(‘Teacher’,(object,),{…})

# 不依赖class关键字创建一个自定义类
# 1. 拿到类名
class_name = 'Teacher'
#2. 拿到类的基类/父类们:(object,)
class_bases = (object,)
#3. 拿到类的名称空间  类的属性 ,方法   也就是类的体代码
class_body = '''
	HP = 100
	def __init__(self,name,age,sex):
    self.name = name
    self.age = age
    self.sex = sex
	def run(self):
    print('%s在跑步'%self.name)
'''
class_dic = {}
# 介绍 exec 函数   可以执行字符串里面的代码
# exec('print(1)')
# exec函数的使用,第一个参数代码放到字符串里面,第二个参数是全局的字典,第三个参数的局部的字典
# exec('x=1',{},{})
# exec函数的使用,我要用第一个参数代码放到字符串里面,不用第二个参数是全局的字典因为类体代码没有这样的需求
# ,用第三个参数的局部的字典
exec(class_body,{},class_dic)
Teacher = type(class_name,class_bases,class_dic)

print(Teacher)

print(Teacher.HP)
print(Teacher.run)
a=Teacher('大海',18,'man')
a.run()

自定义元类Mymeta

class Mymeta(type):
    # 但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
    # type(class_name,class_bases,class_dic)
    # __init__
    def __init__(self,class_name,class_bases,class_dic):
        print(self)
        print(class_name)
        print(class_bases)
        print(class_dic)
# 这里必须写object ,默认的type元类会做,自定义元类的要自己写
class Teacher(object,metaclass=Mymeta):
    # Teacher=type('Teacher',(object,),{...})
    # Teacher=Mymeta('Teacher',(object,),{...})
    school = '图灵'
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def run(self):
        print('%s在跑步'%self.name)

自定义元类控制类的产生
现在可以控制类的产生
1.类名必须用驼峰体
2.类体必须有文档注释,且文档注释不能为空

class Mymeta(type):
    # 但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
    # type(class_name,class_bases,class_dic)
    # __init__
    def __init__(self,class_name,class_bases,class_dic):
        if class_name.islower():
            raise TypeError('类名必须使用驼峰体')
        # print(class_dic.get('__doc__'))
        doc = class_dic.get('__doc__')
        if doc is None or len(doc) == 0 or len(doc.strip('\n '))==0:
            raise TypeError('类的体代码必须有文档注释,且不能为空')
            
# 这里必须写object ,默认的type元类会做,自定义元类的要自己写
class Teacher(object,metaclass=Mymeta):
    '''
    dasdas
    '''
    # Teacher=type('Teacher',(object,),{...})
    # Teacher=Mymeta('Teacher',(object,),{...})
    school = '图灵'
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def run(self):
        print('%s在跑步'%self.name)

自定义元类来控制类的调用产生自定义对象
现在可以控制类的产生
1.类名必须用驼峰体
2.类体必须有文档注释,且文档注释不能为空

class Mymeta(type):
# 但凡继承了type的类才能称之为自定义的元类,否则就是只是一个普通的类
# type(class_name,class_bases,class_dic)
# __init__
# def __init__(self,class_name,class_bases,class_dic):
#     if class_name.islower():
#         raise TypeError('类名必须使用驼峰体')
#     # print(class_dic.get('__doc__'))
#     doc = class_dic.get('__doc__')
#     if doc is None or len(doc) == 0 or len(doc.strip('\n '))==0:
#         raise TypeError('类的体代码必须有文档注释,且不能为空')
def __call__(self, *args, **kwargs):
    # print(self)
    # print(args)
    # print(kwargs)
    # 1. 先产生一个空对象__new__可以给Teacher这个类创建一个空对象
    tea_obj = self.__new__(self)
    # 2. 执行__init__方法,完成对象的初始属性操作
    self.__init__(tea_obj,*args, **kwargs)
    # 3. 返回初始化好的那个对象
    return tea_obj

# 这里必须写object ,默认的type元类会做,自定义元类的要自己写
class Teacher(object,metaclass=Mymeta):
    '''
    dasdas
    '''
    # Teacher=type('Teacher',(object,),{...})
    # Teacher=Mymeta('Teacher',(object,),{...})
    school = '图灵'
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def run(self):
        print('%s在跑步'%self.name)
    # def __call__(self, *args, **kwargs):
    #     print('aaa')
tea1 = Teacher('大海',18,'man')
print(tea1.__dict__)
# tea1()

推理:如果一切皆对象,那么Teacher也是一个对象,该对象之所可以调用,肯定是这个对象的类Mymeta中也定义了一个函数__call__

自定义元类来控制类的调用过程产生对象
元类Mymeta实例化Teacher,调用Teacher实际上是调用元类Mymeta的__call__方法会
1.先产生一个Teacher的空对象tea_obj
2.Teacher执行__init__方法,完成对象的初始属性操作
3.返回初始化好的那个对象
推理:调用Teacher(…)就是在调用Teacher的元类Mymeta中的__call__,那么在该__call__中就需要做上述三件事

自定义元类来控制类的调用(即类的实例化过程)

#1 我们把对象实例化的属性全部变成隐藏属性

class Mymeta(type):

    def __call__(self, *args, **kwargs):
        # print(self)
        # print(args)
        # print(kwargs)
        # 1. 先产生一个空对象__new__可以给Teacher这个类创建一个空对象
        tea_obj = self.__new__(self)
        # 2. 执行__init__方法,完成对象的初始属性操作
        self.__init__(tea_obj,*args, **kwargs)
        # 在这里改
        # _类名__属性
        # _Teacher__name
        # print(tea_obj.__dict__)
        # k1 = []
        # v1 = []
        # # print(self.__name__)
        # # print(tea_obj.__dict__.items())
        # for k,v in tea_obj.__dict__.items():
        #     # print(k,v)
        #     # print('_%s__%s'%(self.__name__,k))
        #     k1.append('_%s__%s'%(self.__name__,k))
        #     v1.append(v)
        # # print(k1)
        # # print(v1)
        # tea_obj.__dict__ = dict(zip(k1,v1))
        # 字典生成式
        tea_obj.__dict__ = {'_%s__%s'%(self.__name__,k):v for k,v in tea_obj.__dict__.items()}
        # 加条件
        # tea_obj.__dict__ = {'_%s__%s' % (self.__name__, k): v for k, v in tea_obj.__dict__.items() if v == '大海'}
        print(tea_obj.__dict__)
        # 列表生成式
        print([i * 3 for i in range(1,10)])
        # 只拿到偶数
        print([i * 3 for i in range(1, 10) if i % 2 == 0])
        # print(tea_obj.__dict__)
        # 3. 返回初始化好的那个对象
        return tea_obj
# 这里必须写object ,默认的type元类会做,自定义元类的要自己写
class Teacher(object,metaclass=Mymeta):
    '''
    dasdas
    '''
    # Teacher=type('Teacher',(object,),{...})
    # Teacher=Mymeta('Teacher',(object,),{...})
    school = '图灵'
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def run(self):
        print('%s在跑步'%self.name)
    # def __call__(self, *args, **kwargs):
    #     print('aaa')
tea1 = Teacher('大海',18,'man')
print(tea1.__dict__)
# print(tea1._Teacher__name)
# tea1()

推理:如果一切皆对象,那么Teacher也是一个对象,该对象之所可以调用,肯定是这个对象的类Mymeta中也定义了一个函数__call__

列表生成式和字典生成式

列表生成式

print([i * 3 for i in range(1,10)])
# 只拿到偶数
print([i * 3 for i in range(1, 10) if i % 2 == 0])

#原始写法
i1 = []
for i in range(1, 10):
    if i % 2 == 0:
        i1.append(i*3)
print(i1)

字典生成式

dict1 = {k+'aaa':v for k,v in {'name':'大海','age':18}.items() if k == 'name'}
print(dict1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值