面向对象(四)

面向对象(四)


上周回顾


派生类

重写:继承一个父类,然后重新定义其功能,使用super可以重新使用父类功能。


封装

将类中的数据或方法隐藏起来,不能直接访问或使用

  1. 必须在类定义阶段隐藏
  2. 隐藏方式为双下划线加变量名函数名
  3. 内部其实是一种变形
  4. 访问或使用该数据需要对外提供访问隐藏数据的接口

伪装

将功能名伪装成数据名

@property


多态

针对多个事物不需要考虑具体的特征,只需要了解其功能

鸭子类型理论:走路像鸭子,叫声像鸭子,那么他就是鸭子。


反射方法

利用字符串操作对象

hasattr() 查找字符串对应的功能或者数据是否存在

getattr() 拿出字符串对应的功能或者数据名

setattr() 为对象增加新的键值对

delattr() 删除对象功能或者数据



今日学习内容


反射实际案例

用注释理解上课案例

# 利用面向对象编写系统终端功能
class WinCmd(object):  # 定义一个类
    def ls(self):  # 定义ls功能
        print('windows系统正在执行ls命令')
    def dir(self):  # 定义dirgonn
        print('windows系统正在执行dir命令')
    def cd(self):  # 定义cd功能
        print('windows系统正在执行cd命令')

class LinuxCmd(object):  # 定义另外一个类
    def ls(self):  # 定义ls功能 
        print('Linux系统正在执行ls命令')
    def dir(self):  # 定义dirgonn
        print('Linux系统正在执行dir命令')
    def cd(self):  # 定义cd功能
        print('Linux系统正在执行cd命令')

obj = WinCmd()  # 创建一个win类的对象
obj1 = LinuxCmd()  # 创建一个linx类的对象
"""反射提供了一种不需要考虑代码的前提下 操作数据和功能"""
def run(obj):  # 定义一个函数,需要接受一个参数,该参数作为对象名
    while True:
        cmd = input('请输入您的指令>>>:')  # 获取用户输入
        if hasattr(obj, cmd):  # 如果用户输入存在于这个对象中
            func_name = getattr(obj, cmd)  # 将他取出赋值给变量名
            func_name()  # 调用该功能
        else:
            print('cmd command not found')  # 打印该功能不存在
run(obj1) 
run(obj)

面向对象的双下方法


面向对象中的一部分双下方法,是达到某个条件就会自动触发的

类似于__init__ 在对象实例化的时候就会自动触发
下面我们来说一下其他双下方法的自动触发并执行

1.__str__
	对对象进行打印操作时执行,必须返回字符串类型的数据,多数用来描述对象

2.__del__
	对象执行完或者被主动删除的时候会执行

3.__getattr__
  查找对象不存在名字的时候执行

4.__setattr__
	为对象添加属性操作的时候执行
 
5.__call__
	对象加括号调用的时候会执行,该方法返回什么,对象加括号就会获得什么

6.__enter__
	对象使用with上下文管理语法自动执行,该方法返回什么as后面的变量名就会得到什么
  
7.__exit__
	对象被执行with上下文管理语法结束之后自动触发,与__enter__配套使用

8.__getattribute__
	查找对象名字就会触发,不管是否存在


笔试题讲解

1.让字典具备句点符查找值的功能
class MyDict(dict):  # 定义一个类继承字典父类
    def __getattr__(self, item):  # __getattr__功能获取用户输入
        return self.get(item)  # 使用点get的方法获取字典数据的k值

    def __setattr__(self, key, value):  #__setattr__增加键值对
        self[key] = value  # 根据字典内置方法,k值存在修改,不存在增加键值对

obj = MyDict({'name':'jason','age':18})

obj.pwd = 123  # 给字典名称空间添加名字  不是数据k:v
print(obj)

2.补全下列代码 使其运行不报错
	"""
	class Context:
			pass
	with Context() as ctx:
			ctx.do_something()
	"""
  class Context:  
    def __enter__(self):  # 使用上下文管理两兄弟
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass
    def do_something(self):
        pass
	with Context() as ctx:
    ctx.do_something()

元类简介

即产生类的类 type

type查看的其实是当前对象所属的类名称
<class 'type'>
'''type就是所有类默认的元类!!!'''

产生类的两种表现形式

一直以来使用的
1.class关键字
	class C1(object):
    pass
	print(C1)  # <class '__main__.C1'>

本质上class也是第二种方式
2.type元类
	type(类名,父类,类的名称空间)
  	res = type('C1', (), {})
		print(res)  # <class '__main__.C1'>

掌握了类产生我们就可以高度定制类的产生行为


元类的基本使用

元类不能通过继承的方式直接指定。需要关键字参数的形式进行修改

  	class C1(metaclass=MyTypeClass):
    	pass

元类的进阶操作

根据__call__的触发条件
5.__call__
	对象加括号调用的时候会执行,该方法返回什么,对象加括号就会获得什么
  
推导:类其实也有__call__方法,类加括号会执行元类的里面的__call__该方法返回什么其实类加括号就会得到什么
  """类里面的__init__方法和元类里面的__call__方法执行的先后顺序"""
  
 	class MyTypeClass(type):  # 定义一个类继承type
    def __call__(self, *args, **kwargs):  # 重写__call__方法
        print('__call__ run')  # 查看__call__什么时候执行
        super().__call__(*args, **kwargs)  # 使用原本功能
  class MyClass(metaclass=MyTypeClass):  # 使用关键字继承
      def __init__(self, name):  # 重写__init__方法
          print('__init__ run')  # 查看__init__什么时候执行  
          self.name = name  # 接受参数
  obj = MyClass('eason')
  
  结果:__call__ run
			 __init__ run
   	说明先产生一个对象先调用了__call__再调用了__init__产生对象。
    
# 定制对象的产生过程
class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        if args:
            raise Exception('必须以参数相等的方式使用所有关键字参数')
        super().__call__(*args, **kwargs)  # 正常产生

class MyClass(metaclass=MyTypeClass):
    def __init__(self, name, age):
        self.name = name
        self.age = age

obj2 = MyClass(name='eason',age=21)

如果你想高度定制类的产生过程,那么编写元类里面的__init__方法。
如果你想高度定制对象的产生过程,那么编写元类里面的__call__方法。

双下new方法

__new__用于产生空对象(类)			骨架
__init__用于实例化对象(类)		血肉

注意:并不是所有的地方都可以直接调用__new__ 该方法过于底层
	如果是在元类的__new__里面 可以直接调用
		class Meta(type):
      def __new__(cls, *args, **kwargs):
          obj = type.__new__(cls,*args,**kwargs)
          return obj
	如果是在元类的__call__里面 需要间接调用
		class Mate(type):
    	def __call__(self, *args, **kwargs):
         obj = object.__new__(self) # 创建一个空对象
         self.__init__(obj,*args,**kwargs) # 让对象去初始化
         return obj
        
现在产生一个对象我们可以知道到底自动调用了几个双下方法
1.__new__
2.__call__
3.__init__

作业

编写元类规定对象的所有数据值转大写

class MyTypeClass(type):  # 定义一个接受type的类
    def __init__(self,name,bases,dic):  
      # 重写双下init方法,写入产生类的默认参数
        if not name.isupper():  # 加上判断条件
            raise Exception('类名必须全大写')


class EASON(metaclass=MyTypeClass): # 正确的情况
    def a(self):
        print('1')
    def b(self, name, age):
        self.name = name
        self.age = age
        print(self.name, self.age)

否则在类定义阶段就会报错
posted @ 2022-04-11 23:05  Eason辰  阅读(29)  评论(0)    收藏  举报