面向对象进阶

【0】习题

1.列出你所知道的魔术方法,分别在什么时候执行
1.__init__在对象实例化时
2.__str__是在打印对象显示内容的时候自动触发
3.__del__程序正常运行结束时被动删除,使用del关键字主动删除,
4.__getattr__对象查找不存在的名字的时候触发
5.__setattr__对象在执行添加属性操作时自动触发
6.__call__对象被加括号调用的时候自动触发obj()
7.__enter__对象被执行with(上下文管理)语法开始自动触发
    该方法返回什么,as后面就会得到什么
8.__exit__对象被执行with(上下文管理)语法结束自动触发
9.__getattribute__只要对象查找名字,无论名字存不存在都会执行该方法
    如果有__getattribute__就不会执行__getattr__
10.__repr__ : 交互解释器会触发
    eg:在交互解释器中创建一个Person对象 就会
11.__doc__ : 打印类里面的注释内容的时候会触发
    eg:用help()查看
12.__setitem__ : 对象[key]=value 设置值的时候会触发--》中括号设置属性的时候触发
13.__getitem__ : 获取属性的时候会触发 获取方式为 对象[key]
14.__delitem__ : 删除属性的时候会触发 删除方式为 del 对象[key]
2.反射有什么用,如何用
涉及到对象和字符串一定用反射
 1.hasattr()==>判断类中有没有字符串对应的数据或函数
 2.getattr()==>根据字符串获取对应把你属性值或函数地址<function Student.get at 0x10527a8c8>,函数的运行值
 3.setattr()==>根据字符串给的对象设置键值对,新增,键值对,第二个是键,第三个是值
    语法:setattr(对象名,要删的函数名或属性名即名称空间字典中的键,要换的函数名或属性名(变量))
    注意:有则修改,无则新增
  4.delattr()==>根据字符串删除对象对应的键值对==》给键
    语法:delattr(对象名,要删的函数名或属性名即名称空间字典中的键)

【一】绑定方法

在类中定义的函数默认都是绑定给对象使用的
    即对象来调 会自动将对象当做第一个参数传入
    self自动传入当作对象

【二】init魔法方法推导过程

# 推导步骤1  一步步自己手动添加
  obj1.__dict__['name'] = 'jason'
  obj2.__dict__['name'] = 'kevin'
# 推导步骤2  封装成函数减少代码冗余
    def set_info(obj, name):
    obj.__dict__['name'] = name
# 推导步骤3  将函数整合到类体代码中
    class Student:
    def set_info(obj, name):
      obj.name = name
# 推导步骤4  python提供自动出发的方法
    class Student:
    def __init__(obj, name):
      obj.name = name
__init__如何执行
1.类加括号先产生一个空对象
2.将类括号内的参数和空对象交给__init__执行
    空对象作为第一个参数
3.该方法会给对象添加独有的数据并自动返回对象本身

【三】动态静态方法

1.动态(自动调用)两种(不用传参)
  a.绑定给对象(普通)
  对象调用绑定给对象的方法:会自动将对象当做第一个参数传入self
  # (1)对象可以直接调用绑定给对象的方法
        s = Student('dream')
        s.talk() # 默认将 s 作为 self 自动传入
  # (2)类调用绑定给对象的方法,需要主动传入一个生成的对象
        Student.talk(s)
  b.绑定给类
  类调用绑定给类的方法:会自动将类当做第一个参数传入cls
  eg:class Student:
      @classmethod
      def eat(cls):
        prtin(cls)
   # (1)对象调用绑定给类的方法, 默认将实例化得到当前对象的类自动传入
        stu.read('dream')直接调
   # (2)类调用绑定给类的方法,类可以直接调用,默认将调用当前方法的类自动传入
        Student.read('dream')
2.静态方法(就是普通的函数)有几个参就传几个参数两种
    a.普通函数
    b.类中@staticmethod
    class Student:
        @staticmethod
      def speak(a):
          print(a)

【四】继承

'''以后看到self点东西 一定要问自己self是谁'''
'''需明确self就是当前实例化的对象'''
1.就是获得父亲的所有东西
2.使用:class A(B):  #B是父类,A是B的子类
            pass
       class A(B,C,D):
            pass
3.一个类可以继承多个父类
  一个类也可以被多个子类继承
4.在面向对象编程中 其实类和父类的主要功能都是用来减少代码冗余的
    对象:数据与功能的结合体
    类:多个对象相同数据和功能的结合体
    父类:多个类相同数据和功能的结合体
5.关于继承是名字的查找顺序
'''名字的查找顺序永远都是 先从当前对象自身开始查找'''
  a.单继承==》对象    >>> 类   >>> 父类
  b.多继承===》
    没有形成闭环的情况下,深度优先遍历
    形成闭环式先先用深度走完没闭环最后走那个点
6.MRO决定了在多继承情况下,方法和属性的解析顺序。

【五】派生类

1.子类调用父类的初始化方法,但是子类中的属性要比父类多怎么办
def __init__(self,写上你所有需要的属性)
    完整写法《==》super(自己类名,self).__init__(父类中你需要的属性)
    精简写法《==》super().__init__(父类中你需要的属性)
    父类中没有你需要的属性也要写上
    self.属性名=属性值
​
如果自己写的子类需要使用父类的方法 并且还需要基于该方法做扩展
这样的子类我们称之为派生类(本质还是子类)      
    那么可以使用super关键字来实现
语法:
    super().方法名(父类中你需要东西)
主要记住super是在父类的基础上进行扩展
class Myclass(list):
    def append(self,args):
        if args=='111':
            print("对不起,不能追加")
        else:
            super(Myclass,self).append(args)
s=Myclass()
s.append("111")
print(s)
为什么s直接就是列表?
首先明确init方法是自动调用的
在你实例化对象时,类中没有init那么就会去父类中看是否有init然后自动调用
他就变成列表了

【六】继承下的派生实际应用

# 改写json,datatime对象写不进去
import json
import datetime
from typing import Any
class modify(json.JSONEncoder):
    def default(self, o: Any) -> Any:
        if isinstance(o,datetime.datetime):
            return o.strftime("%Y-%m-%d %X")
        elif isinstance(o,datetime.date):
            return o.strftime("%Y-%m-%d")
        else:
            super().default(o)

【七】封装

1.把一些东西(属性,函数)藏起来不让别人看见,不让外界调用--》eg:__name
	利用双下划线
2.其实就是变形了--》在外面__属性也获得不了,查看名称空间__dict__==》他变形成了_类名__属性,但不允许在外部这样查看
3.并没有真正的隐藏  而是自动转换成了特定的语法
	我们虽然指定了封装的内部变形语法 但是也不能直接去访问
    看到了就表示这个属性需要通过特定的通道(接口)去访问
4.那么不允许直接利用名称空间访问以及操作,就要在类内提供响应的接口(功能函数)让外部间接的操作数据,在接口里可以直接__属性进行改值,查看等操作
eg:
a.函数
	class Atm():
		def __card(self):
			print("请插卡")
		def__auth(self):
			print("用户认证")
		def withdraw(self):
			self.__card()
			self.__auth()
    obj=Atm()
    obj.withdraw()

b.属性
	class Student(object):
    __school = '清华大学'
    def __init__(self, name, age):
        self.__name = name
        self.__age = age
    # 专门开设一个访问学生数据的通道(接口)
    def check_info(self):
        print("学生姓名:%s,学生年龄:%s  %(self.__name,self.__age))
    def set_info(self,name,age):
        self.__name = name
        self.__age = age
1.property就是将方法伪装成数据==》比如说你不会觉得BMI是一个功能,你只会觉得他是一个数据,你就应该用@property

【八】多态

多态可以借助抽象类实现,因为抽象的方法必须重写
1.不同动物,不同叫法,应该调用同一种方法,应该把叫的方法墨守成规的叫同一种方法,这样就不用管他们叫的不一样的函数方法了都把叫的函数叫speak()
eg:列表字符串元组,统计长度的方法都是len(),这就是多态性,减少操作的复杂度
2.需要自己取遵守,与父类中的一样的speak()
3.记住一个类的方法就可以操作所有

【九】反射

1.对象与字符串之间的操作
2.什么时候用,涉及到对象和字符串一定用反射
3.提取关键字
记住:
    1.hasattr()==>判断类中有没有字符串对应的数据或函数
    2.getattr()==>根据字符串获取对应把你属性值或函数地址<function Student.get at 0x10527a8c8>,函数的运行值
    3.setattr()==>根据字符串给的对象设置键值对,新增,键值对,第二个是键,第三个是值
    语法:setattr(对象名,要删的函数名或属性名即名称空间字典中的键,要换的函数名或属性名)
    注意:有则修改,无则新增
    4.delattr()==>根据字符串删除对象对应的键值对==》给键
    语法:delattr(对象名,要删的函数名或属性名即名称空间字典中的键)
eg:
	class Student(object):
    school='清华大学'
    def get(self):
        pass
s=input("请输入功能")
# s是Student类中的功能或数据
s1=Student()
def index():
    print("xxx")
if hasattr(Student,s):
        #     获取该数据的值或函数名
        #     清华大学
        #     <function Student.get at 0x10527a8c8>
        getattr(Student,s)
        print(Student.__dict__)
#         改对应数据名的值,或把函数名换进去了变成另一个函数
#         'get': <function index at 0x0000024315F03E20>
        setattr(Student,s,"index")

        print(Student.__dict__)
案例1
判断操作时写入还获取,随后根据字符串cmd,获取对象self对应的方法属性会输出
"""Downloading %s...' %file"""
class FtpServer:
     def serve_forever(self):
         while True:
             inp=input('input your cmd>>: ').strip()
             cmd,file=inp.split()
             if hasattr(self,cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
                 func=getattr(self,cmd) # 根据字符串cmd,获取对象self对应的方法属性
                 func(file)
     def get(self,file):
         print('Downloading %s...' %file)
     def put(self,file):
         print('Uploading %s...' %file)

obj = FtpServer()
obj.serve_forever()
案例2
# 利用面向对象编写系统终端功能
class WinCmd(object):
    def ls(self):
        print('windows系统正在执行ls命令')
    def dir(self):
        print('windows系统正在执行dir命令')
    def cd(self):
        print('windows系统正在执行cd命令')

class LinuxCmd(object):
    def ls(self):
        print('Linux系统正在执行ls命令')
    def dir(self):
        print('Linux系统正在执行dir命令')
    def cd(self):
        print('Linux系统正在执行cd命令')

obj = WinCmd()
obj1 = LinuxCmd()
"""反射提供了一种不需要考虑代码的前提下 操作数据和功能"""
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)

【十】面向对象双下方法

1.类中__str__是在打印对象显示内容的时候自动触发,且必须返回一个字符串类型
	如果类中有写该方法,那么打印对象名打印出来的是str的返回值
	
2.__del__程序正常运行结束时被动删除,使用del关键字主动删除,可以删除类里的方法,但是不能删除对象和静态方法

3.__getattr__对象查找不存在的名字的时候触发,打印查不到的名字,会返回__getattr__的返回值

4.__setattr__对象在执行添加属性操作时自动触发

5.__call__对象被加括号调用的时候自动触发obj(),类本身就能被call所以不需要写这个方法

6.__enter__对象被执行with(上下文管理)语法开始自动触发
	该方法返回什么,as后面就会得到什么

7.__exit__对象被执行with(上下文管理)语法结束自动触发

8.__getattribute__只要对象查找名字,无论名字存不存在都会执行该方法
	如果有__getattribute__就不会执行__getattr__
    
9.__init__实例化对象的时候触发

10.__repr__ : 交互解释器会触发
    # 在交互解释器中创建一个Person对象 就会
    
11.__doc__ : 打印类里面的注释内容的时候会触发
    用help()查看
    
12.__setitem__ : 对象[key]=value 设置值的时候会触发--》中括号设置属性的时候触发
13.__getitem__ : 获取属性的时候会触发 获取方式为 对象[key]

14.__delitem__ : 删除属性的时候会触发 删除方式为 del 对象[key]
双下笔试题
1.让字典具备句点符查找值的功能
	# 1.定义一个类继承字典
 
 class MyDict(dict):
      def __getattr__(self, item):#自动触发为了获取到字典中的数据
          return self.get(item)

      def __setattr__(self, key, value):#对象添加属性时操作
      #执行obj.pwd = 123一定会调用
          self[key] = value#设置了键值对
  '''要区别是名称空间的名字还是数据k:v键值对'''
  obj = MyDict({'name':'jason','age':18})
  # 1.具备句点符取v
  # print(obj.name)
  # print(obj.age)
  # 2.具备句点符设k:v
  # obj['gender'] = 'male'
  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()

【十一】元类

1.即产生类的类
2.type其实找的是当前对象的类名称
3.type是所有类默认的类
4.产生类的两种方式
	a.class关键字
	b.type元类----》返回值也是类
		type(类名,父类,类的名称空间)
  		res = type('C1', (), {})
		print(res)  # <class '__main__.C1'>
5.学习元类目的--》控制类创建,高度定制类的行为
6.比如:要求类的名字必须首字母大写
		思考在哪里编写定制化代码
			类的产生过程目前还比较懵 	  元类里面的__init__方法
			对象的产生过程呢 			     类里面的__init__方法
		方法:由已知推未知
7.元类的基本使用
	元类不能继承
    class MyTypeClass(type):
        def __init__(cls, cls_name, cls_bases, cls_dict):
            # print(cls, cls_name, cls_bases, cls_dict)
            if not cls_name.istitle():
                raise Exception("类名的首字母必须大写 你个SD")
            super().__init__(cls_name, cls_bases, cls_dict)

    class C1(metaclass=MyTypeClass):#可以
        school = '清华大学'

    class a(metaclass=MyTypeClass):#报错
        school = '清华大学'
元类进阶
1.回想__call__方法
	对象加括号会自动执行产生该对象的类里面的__call__,并且该方法返回什么对象加括号就会得到什么
  推导:类加括号会执行元类的里面的__call__该方法返回什么其实类加括号就会得到什么
  """类里面的__init__方法和元类里面的__call__方法执行的先后顺序"""
 	class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        print('__call__ run')
        super().__call__(*args, **kwargs)
  class MyClass(metaclass=MyTypeClass):
      def __init__(self, name):
          print('__init__ run')
          self.name = name
  obj = MyClass('jason')
  
# 定制对象的产生过程
	class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        # print('__call__ run')
        # print(args,kwargs)
        if args:
            raise Exception('必须全部采用关键字参数')
        super().__call__(*args, **kwargs)

  class MyClass(metaclass=MyTypeClass):
      def __init__(self, name):
          # print('__init__ run')
          self.name = name

	"""强制规定:类在实例化产生对象的时候 对象的独有数据必须采用关键字参数"""
  # obj1 = MyClass('jason')
  obj2 = MyClass(name='jason')
"""
如果你想高度定制类的产生过程
	那么编写元类里面的__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
"""

【十三】抽象

# 将某几个具体的生物,根据特征总结成一个类,逐层向上总结
所有继承父类的子类必须重写父类的某些方法,这个父类就叫抽象类
***父类中要继承元类metaclass=abc.ABCMeta,如果要子类必须重写在父类的方法前加 @abc.abstractmethod
import abc
class Animal(metaclass=abc.ABCMeta):

    def __init__(self, color, foot, hand):
        self.color = color
        self.foot = foot
        self.hand = hand

    def speak(self):
        print(f'任何动物都能叫')

    # 在子类中必须重写父类的当前方法
    @abc.abstractmethod
    def walk(self):
        ...

class BlackBear(Animal):
    def __init__(self, color, foot, hand):
        super().__init__(color, foot, hand)

    # 如果不重写父类的方法就会报错
    # Can't instantiate abstract class BlackBear with abstract methods walk
    def walk(self):
        ...

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值