首页 > 其他分享 >元类的认识和基础用法

元类的认识和基础用法

时间:2023-08-03 19:33:23浏览次数:31  
标签:LOG 认识 object 用法 元类 基类 MyClass type


元类

“元类就是深度的魔法,99%的⽤户应该根本不必为此操⼼。
如果你想搞清楚 究竟是否需要⽤到元类,那么你就不需要它。
那些实际⽤到元类的⼈都⾮常 清楚地知道他们需要做什么,⽽且根本不需要解释为什么要⽤元类。“——蒂姆·彼得斯TimPeters

什么是元类

在python中,所有的类,都是基于元类创建的。

class demo(object):
    pass

在python中,一切都是对象,类也是对象,所以一个类必定会有一个类型。

此处的object是所有python类层次结构的基类,也就是说所有的类都是继承它的。

那么,object又是什么类型?

print(type(object))

我们打印出来看一下:

<class 'type'>

显示的就是元类。

我们回到前面说的那句话,再加上注解会更容易理解了。

  • 在python中,一切都是对象(object),类(class)也是对象(object),所以一个类(class)必定会有一个类型(type)。
  • 用来创建类的类,叫做元类,函数type实际上也是一个元类。
  • python中任何形式类以及python3中的任何类都是type元类的一个实例。

需要注意的是,我们要区分元类和继承的基类:

  • type:是元类,所有的类都是通过type所创建出来的
  • object:顶层的基类,所有类的继承顶层父类都是object
  • type是创造者女娲,object是女娲创造的第一个孩子。(不是很准确,但这样好理解)

参数详解

type源码

class type(object):
    """
    type(object_or_name, bases, dict)
    type(object) -> the object's type
    type(name, bases, dict) -> a new type
    """

源码中写道,使用type(name, bases, dict)就可以定义一个新的元类。

参数详解

  • name : 表示要创建的类的名称。(字符串类型)
  • bases : 继承类的基类元组(或包含基类的元类)。(元组类型)
  • dict : 类属性和方法。(字典类型)

自定义类与元类创建的比对

我们自己创建类,代码如下

class MyClass(object):
    x = 42

obj = MyClass()
print(obj.x) # 输出42

这里,类名为MyClass,继承了基类object,属性为x=42

那么我们就按照元类创建类的方式构造一个和如上类一样功能的类。

MyClass = type('MyClass',(object,),{"x":42})
obj = MyClass()
print(obj.x) #输出42

由于object是默认继承的基类,bases参数可以为空,代码如下:

MyClass = type('MyClass',(),{"x":42})
obj = MyClass()
print(obj.x) #输出42

既然我们可以自己创建类,为什么要用元类?

别问,问就回去文章开头TimPeters说的那句话。

深入一下

经过上面的例子我们知道了,object是所有类的基类,而type是创建类的类,那么我如果把基类修改了,是不是创建出来的类就是自动继承了我所修改后的基类?

定义一个元类

  • 声明一个类,并继承自type类。
  • 在元类中定义__new__方法,该方法用于创建新的类。
  • __new__方法中可以自定义类的行为、属性和方法。
class MyMeta(type):
    def __new__(meta, name, bases, attrs):
        # 自定义类的行为
        print("想不到吧,我才是基类")
        # 创建并返回新的类
        return super().__new__(meta, name, bases, attrs)

使用元类动态创建类

在创建类时,可以使用metaclass参数指定所使用的元类。

声明一个普通的类,并将metaclass参数设置为定义的元类,metaclass默认为type

class MyClass(metaclass=MyMeta):
    # 类的定义
    hh = 123
    print("呜呜呜,我就是一个普通的类,可我的元类不是type了,而是自定义的")

运行以上代码:

aa = MyClass()
print(aa.hh)
#输出结果如下>>>
呜呜呜,我就是一个普通的类,可我的元类不是type了,而是自定义的
想不到吧,我才是基类
123

如果我们没有指定元类为自定义的元类,输出将会没有想不到吧那一行。

class MyClass2():
    # 类的定义
    h = 123
    print("呜呜呜,我就是一个普通的类,可我的元类不是type了,而是自定义的")

aa = MyClass2()
print(aa.h)
#输出结果如下>>>
呜呜呜,我就是一个普通的类,可我的元类不是type了,而是自定义的
123

用元类写一个简单的日志记录器

class LogMeta(type):
    def __new__(meta, name, bases, attrs):
        # 检查属性中的日志消息
        logs = {}
        for key, value in attrs.items():
            if isinstance(value, str) and value.startswith("LOG "):
                logs[key] = value
        
        # 添加日志方法
        for key in logs.keys():
            def log_func(self, message):
                print(f"Log {key}: {message}")
            attrs[key] = log_func
        
        # 创建并返回新的类
        return super().__new__(meta, name, bases, attrs)


class MyLogger(metaclass=LogMeta):
    LOG_INFO = "LOG INFO"
    LOG_ERROR = "LOG ERROR"
    LOG_WARNING = "LOG WARNING"

在上面的示例中,我们创建了一个LogMeta元类,它通过检查类属性中的日志消息并动态创建日志方法。然后我们使用LogMeta元类创建了一个MyLogger类,并在其中定义了几个日志消息。最后,我们可以使用MyLogger类创建对象并调用日志方法。

logger = MyLogger()
logger.LOG_INFO("This is an informational message.")
logger.LOG_ERROR("An error occurred.")

当我们运行上面的代码时,它将输出以下内容:

Log LOG_WARNING: This is an informational message.
Log LOG_WARNING: An error occurred.

下一节我们讲讲如何将它运用在我们的测试框架里面动态创建测试用例。


标签:LOG,认识,object,用法,元类,基类,MyClass,type
From: https://blog.51cto.com/u_15800928/6952313

相关文章

  • C++ | extern "C" 的用法
    extern"C"是C++中的一种用法(无法在C语言中使用),其作用是修饰一段代码,将其用C语言的方式进行编译。那么,使用C语言方式进行编译与使用C++方式进行编译又有什么区别呢?在C++中,支持函数重载:voidfunc();voidfunc(inta,intb);voidfunc(doublea,floatb);但在C语言中如果......
  • Activity之间数据交流一(startActivityForResult , onActivityResult , setResult 的用
    主Activity)上能连接往许多不同子功能模块(子Activity上去),当子模块的事情做完之后就回到主界面,或许还同时返回一些子模块完成的数据交给主Activity处理。这样的数据交流就要用到主Activity回调函数onActivityResult()。一些方法: 主Activity中:<1>主Activity......
  • curl常见用法以及查看响应时间
    curl是一个很强大的工具,支持模拟http请求,语法如下:curl[options][URL...]最简单的用法➜~curlhttp://www.baidu.com最常用法-H:代表header头-X:是指定什么类型请求(POST/GET/HEAD/DELETE/PUT/PATCH)-d:代表传输数据curl-H"Content-Type:application/json"-XPOST......
  • 算法笔记(二)—— 认识N(logN)的排序算法
    递归行为的时间复杂度估算整个递归过程是一棵多叉树,递归过程相当于利用栈做了一次后序遍历。对于master公式,T(N)表明母问题的规模为N,T(N/b)表明每次子问题的规模,a为调用次数,加号后面表明,除去调用之外,剩余语句的复杂度是多少,算出d。根据上次三个判断公式进行算法时间复杂度计算......
  • 粗略认识分层结构中的各种O (DTO VO BO PO DO)
    DTO(DataTransferObject)数据传输对象这个传输通常指的是前端与后端之间的传输,因此通常作为用于展示层与服务层之间的数据传输对象。但在微服务盛行的当下,服务和服务之间调用的传输对象也使用DTO.如下图中调用远程业务时返回DTO对象.而且前端传送给后端的数据使用Q......
  • IOC认识及Autofac使用
    依赖注入学习DIP(DependencyInversion Principle)依赖倒置原则:上层模块不应该依赖于底层模块,二者应该通过抽象来依赖,依赖抽象而不是依赖细节。换言之,要针对接口编程,而不是针对实现编程。IOC(InversionofControl)控制反转:面向对象编程中的一种设计原则,可以用来减低计算机代......
  • 2.auto、decltype和decltype(auto)的用法
    2.auto、decltype和decltype(auto)的用法1.auto编程时常常需要把表达式的值赋给变量,这就要求声明变量时清楚的知道表达式的类型。然而有些情况是声明的变量的类型我们并不知道,比如在模板编程时。为了解决这个问题,C++11引入了auto类型说明符,用它来让编译器替我们去分析表达式所属......
  • Java中synchronized的用法
    在Java中,synchronized是一种同步机制,可用于控制多个线程在访问共享资源时的并发问题。synchronized可以修饰方法和代码块,以确保共享资源的互斥访问,从而避免不同线程间访问该资源时发生冲突。synchronized的用法包括以下几种:同步方法使用synchronized修饰方法,可以确保在多个线程访问......
  • @RequiredArgsConstructor 用法
    在我们写controller或者Service层的时候,需要注入很多的mapper接口或者另外的service接口,这时候就会写很多的@Autowired注解,代码看起来很乱lombok提供了一个注解:@RequiredArgsConstructor(onConstructor=@_(@Autowired))写在类上可以代替@Autowired注解,需要注意的是在注入时需要......
  • 认识开源许可证——Apache2.0
    「七彩话合规」是棱镜七彩推出的全新内容板块,涵盖开源合规基础知识、常见许可证翻译、常见合规问答等多个模块。棱镜七彩开源合规部门专注于开源许可证研究,为您提供权威的许可证翻译参考和问题解答。开源合规第一步:理解开源许可证开源许可证,其本质是一种格式合同,通过特定的条款授予......