python学习笔记

昨日内容回顾

  • 派生方法实战演练
子类继承父类 并且在子类中定义了与父类相同的名字的方法(重写父类方法)
class A:
    def func1(self):
        pass
class B(A):
    def func1(self):
       pass
单纯的重写方法名没有实际意义 还需要在子类方法中重写调用父类的方法
    super().func1()
ps:课下了解super在复杂的写法

实战案例:json序列化非默认数据类型
"""
为什么使用派生而不是直接修改源码
    为了解耦合 并且直接修改源码可能会对其他程序员造成困扰
"""
  • 三大特性之封装
最笼统的说 其实我们定义一个类就是在封装
最直观的看 就是在定义类的时候利用双下划线 隐藏名字

1.前面有两个下划线的名字 实际会变形 _类名__名字
2.封装的目的是为了开设特定的功能去操作这些数据
    我们就可以在特定的功能中添加额外的操作

property伪装方法
"""
@classmethod
@staticmethod
@property
"""
  • 三大特性之多态
一种事物的多种形态
	无论有多少中事物 只要属于一个形态那么针对相同的操作应该有相同的方法
鸭子类型
	一切皆对象
	一切皆文件
  • 面向对象之反射
通过字符串操作对象的属性和方法

hasattr()
getattr()
setattr()
delattr()

反射实战应用
	反射其实就可以让用户和代码更好的交互起来

今日内容概要

  • 反射实战案例
  • 面向对象魔法方法
  • 魔法方法笔试题
  • 元类简介
  • 产生类的两种方式
  • 元类基本使用
  • 元类进阶
  • 双下new方法

今日内容详细

反射实战案例

1.加载配置文件纯大写的配置
    # 配置文件加载:获取配置文件中所有大写的配置 小写的直接忽略  组织成字典
    import settings
    new_dict = {}
    # print(dir(settings))  # dir获取括号中对象可以调用的名字
    # ['AGE', 'INFO', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'desc', 'name']
    for i in dir(settings):
        if i.isupper():  # 如果名字是纯大写 那么获取该大写名字对应的值   'AGE'   'INFO'
            v = getattr(settings, i)
            new_dict[i] = v
    print(new_dict)
2.模拟操作系统cmd终端执行用户命令
    class WinCmd(object):
        def dir(self):
            print('dir获取当前目录下所有的文件名称')

        def ls(self):
            print('ls获取当前路径下所有的文件名称')

        def ipconfig(self):
            print('ipconfig获取当前计算机的网卡信息')
    obj = WinCmd()
    while True:
        cmd = input('请输入您的命令>>>:')
        if hasattr(obj, cmd):   # 根据用户输入的cmd,判断对象obj有无对应的方法属性
            cmd_name = getattr(obj, cmd)  # 根据字符串cmd,获取对象obj对应的方法属性
            cmd_name()
        else:
            print('%s 不是内部或外部命令,也不是可运行的程序或批处理文件' % cmd)

面向对象魔法方法

魔法方法其实就是类中定义的双下方法,之所以会叫魔法方法原因是这些方法都是到达某个条件自动触发 无需调用

魔法方法 触发条件
__ init__ 实例化对象的时候
__ str__ 对象被执行打印操作的时候
__ call__ 对象加括号调用的时候
__ getattr__ 对象获取一个不存在的属性名的时候
__ setattr__ 对象操作属性值的时候
__ del__ 对象在被删除(主动 被动)的时候
__ getattribute__ 对象获取属性的时候自动触发,无论这个属性存不存在
__ enter__ 对象被with语法执行的时候
__ exit__ 对象被with语法执行并运行完with子代码的时候
class MyClass(object):
    def __init__(self, name):
        """实例化对象的时候自动触发"""
        print('__init__方法')
        self.name = name
    def __str__(self):
        """
        对象被执行打印操作的时候会自动触发
            该方法必须返回一个字符串
            返回什么字符串打印对象之后就展示什么字符串
        """
        print('__str__方法')
        print('这是类:%s 产生的一个对象')
        return '对象:%s'%self.name
    def __call__(self, *args, **kwargs):
        """对象加括号调用 自动触发该方法"""
        print('__call__方法')
        print(args)
        print(kwargs)
    def __getattr__(self, item):
        """当对象获取一个不存在的属性名 自动触发
            该方法返回什么 对象获取不存在的属性名就会得到什么
            形参item就是对象想要获取的不存在的属性名
        """
        print('__getattr__', item)
        return '您想要获取的属性名:%s不存在'%item
    def __setattr__(self, key, value):
        """对象操作属性值的时候自动触发>>>:  对象.属性名=属性值"""
        print("__setattr__")
        print(key)
        print(value)
        super().__setattr__(key, value)
    def __del__(self):
        """对象在被删除(主动 被动)的时候自动触发"""
        print('__del__')
        pass
    def __getattribute__(self, item):
        """对象获取属性的时候自动触发 无论这个属性存不存在
            当类中既有__getattr__又有__getattribute__的时候 只会走后者
        """
        print('__getattribute__')
        return super(MyClass, self).__getattribute__(item)  # 复杂写法
        return super().__getattribute__(item)  # 简便写法
    def __enter__(self):
        """对象被with语法执行的时候自动触发 该方法返回什么 as关键字后面的变量名就能得到什么"""
        print('__enter__')
    def __exit__(self, exc_type, exc_val, exc_tb):
        """对象被with语法执行并运行完with子代码之后 自动触发"""
        print('__exit__')

魔法方法笔试题

"""补全以下代码 执行之后不报错"""
class Context:
    pass
with Context() as f:
    f.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 f:
    f.do_something()

元类简介

所有的对象都是实例化或者说是通过调用类而得到的,python中一切皆对象,通过class关键字定义的类本质也是对象,对象又是通过调用类得到的,因此通过class关键字定义的类肯定也是调用了一个类得到的,这个类就是元类。

元类就是用来实例化产生类的类,因此我们可以得到如下的关系:

image

"""
基础阶段我们使用type来查找数据的数据类型
但是学了面向对象之后 发现查看的不是数据类型 而是数据所属的类

我们定义的数据类型 其实本质还是通过各个类产生了对象
    class str:
        pass
    h = 'hello'  str('hello')

我们也可以理解为type用于查看产生当前对象的类是谁
"""
class MyClass:
    pass
obj = MyClass()
print(type(obj))  # 查看产生对象obj的类:<class '__main__.MyClass'>
print(type(MyClass))  # 查看产生对象MyClass的类:<class 'type'>
"""
通过上述推导 得出结论 自定义的类都是由type类产生的
我们将产生类的类称之为 '元类'
"""

产生类的两种方式

第一种比较普遍的方式:

 class Work():
     def __init__(self,name):
         self.name = name
 
 w = Work('well woker')

这样就简单创建了一个类,分别打印w的类型和Work的类型

 print(type(w))    #<class '__main__.Work'>
 print(type(Work))  #<class 'type'>

这就说明type也是一个类,并且是类的类,称为元类,元类就是创建类的类 。

第二种创建类的方式:

用type动态创建类,type有一个方法: type(类名,父类名,类的名称空间) 用来动态创建类

三个参数的具体内容:

   1. 第一个参数: 要创建的类的名称

   2. 第二个参数: 继承的父类集合,Python支持多重继承,如果只有一个父类,写(obj,)

   3. 第三个参数: 类的方法名字典,字典内容为'类里面方法名称' : '写好的函数名称' 创建之后使用类方法名称调用

 def func(self):   #第一个方法
     print('hello %s' % self.name)

 def __init__(self,name,age):   #初始化方法
     self.name = name
     self.age = age

 #type('叫什么类','继承谁','里面有什么方法')

 Foo = type('Foo',(object,),{'eat':func,
                                         '__init__':__init__
                                       })

 f = Foo('周润发','20')
 f.talk()
 print(type(Foo))

元类基本使用

class MyMetaClass(type):
    pass
"""只有继承了type的类才可以称之为是元类"""
class MyClass(metaclass=MyMetaClass):
    pass
"""如果想要切换产生类的元类不能使用继承 必须使用关键字metaclass声明"""

'''
思考
	类中的__init__用于实例化对象
	元类中__init__用于实例化类
'''

class MyMetaClass(type):
    def __init__(self,what, bases=None, dict=None):
        # print('what', what)  类名
        # print('bases', bases) 类的父类
        # print('dict', dict) 类的名称空间
        if not what.istitle():
            # print('首字母必须大写 你会不会写python 面向对象学过吗 lowB')
            raise Exception('首字母必须大写 你会不会写python 面向对象学过吗 lowB')
        super().__init__(what, bases, dict)
"""只有继承了type的类才可以称之为是元类"""


"""如果想要切换产生类的元类不能使用继承 必须使用关键字metaclass声明"""
class aaa(metaclass=MyMetaClass):
    pass

元类进阶

"""元类不单单可以控制类的产生过程 其实也可以控制对象的!!!"""
1.对象加括号执行产生该对象类里面的双下call
2.类加括号执行产生该类的元类里面的双下call
class MyMetaClass(type):
    def __call__(self, *args, **kwargs):
        print('__call__')
        if args:
            raise Exception('必须用关键字参数传参')
        super().__call__(*args, **kwargs)


class MyClass(metaclass=MyMetaClass):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('__init__')


# 需求:实例化对象 所有的参数都必须采用关键字参数的形式
obj = MyClass('jason', 18)  # __call__
obj = MyClass(name='jason', age=18)  # __call__ __init__


总结
"""
如果我们想高度定制对象的产生过程
	可以操作元类里面的__call__
如果我们想高度定制类的产生过程
	可以操作元类里面的__init__
"""

双下new方法

双下new是用于产生空对象用的,它会返回一个空对象。双下init和双下new执行都会产生一个空对象,这个空对象就是由双下new产生的。

"""
类产生对象的步骤
	1.产生一个空对象
	2.自动触发__init__方法实例化对象
	3.返回实例化好的对象
"""
__new__方法专门用于产生空对象			骨架
__init__方法专门用于给对象添加属性	   血肉

如果是在元类的__ 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

作业:

#   自定义字典并且让字典具备
# 	d.key = value  修改键值对
# 	d.key = value  添加键值对


class Dict(dict):

    def __getattr__(self, item):
        return self.get(item)

    def __setattr__(self, key, value):
        self[key] = value

d = Dict({'name': 'xxx', 'age': 20})
d.name = 'wjl'
d.age = 18
print(d)  # {'name': 'wjl', 'age': 18}

d.gender = 'male'
print(d)  # {'name': 'wjl', 'age': 18, 'gender': 'male'}
posted @   空白o  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示