(Python):元类(metaclass), Type类,类实例,dataclass

文章目录

    • 类实例
      • 类成员变量与成员变量的区别
    • Type类
    • Metaclass
      • ABCMeta
    • dataclass

类实例

在python中,所有的类都是一个实例,也就是说,所有的类其实都是由自己的属性和方法的。
这里就需要区分清楚一件事,类实例和对象。类实例指的是类本身,而对象则是类的某一个实例。

class Test:
	pass
x = Test()
print(Test, x)

其中x是Test的对象,而Test本身是类实例。

类成员变量与成员变量的区别

既然类本身是一个实例,那么类实例本身也有自己的成员变量。我们厂有一种错觉认为类实例的成员变量会被分配给每一个该类的对象,实则不然。

类成员变量属于类的属性,类的对象虽然可以访问,但是无法修改,一但对象尝试修改,Py就会自动的在对象中创建一个同名成员变量,然后赋值。此时这个对象不再访问类实例的成员变量。看下面的例子:

class test:
	x = False
	def __init__(self):
		self.y = 2

上述中x是类变量,y是成员变量。他们有如下规则:

  1. 类变量可以通过,类和实例访问,但只允许通过类修改
  2. 如果通过实例修改类变量,则会自动在实例的作用于域中创建一个对应的成员变量
class test:
    ok = True
    @classmethod
    def change_ok(cls):
        cls.ok = False

x, y = test(), test()

print('x, y分别是', x.ok, y.ok)
x.change_ok()
print('x, y分别是', x.ok, y.ok)
x.ok = True
print('x, y分别是', x.ok, y.ok)
x.change_ok()
print('x, y分别是', x.ok, y.ok)
"""
output:
x, y分别是 True True
x, y分别是 False False
x, y分别是 True False
x, y分别是 True False
"""

我们一步一步来看输出:

  1. 首先由于x和y都是test的实例,所以在访问ok时都访问的是类变量ok
  2. 改变类变量ok,由于x和y都访问的是类变量,所以两者同时该变
  3. 为修改x的ok为True,此时Python自动为x的成员变量区域创建一个ok变量,从此之后x不再访问公共类变量ok,而是自己独有的成员变量ok。
  4. 改变类变量ok,此时x已经不会再跟着变了,因为x有了自己的成员变量ok

类变量只能通过类对象访问,而类对象通过:类名.xxx,cls.xxx的方式使用。类对象全局唯一。

Type类

上面讲完了类实例,那么问题来了,Py的类都是一个实例,那么谁创造这些实例呢?
没错,就是Type类创造的,Type类创造了所有的类实例,所有的类实例都是Type的一个instance,看下面代码:

class Test:
    pass
print(isinstance(object, type))
print(isinstance(Test, type))

输出结果均为True,可以见得所有的类都是type类的一个对象。那么是否可以通过type类来创建一个新的类呢?
当然可以!
我们可以通过如下方法来动态的创建一个类

type(类名, (父类们), 所有属性)

举个例子:

@classmethod
def speak(cls, x):
	print(x)
T = type('Myclass', (object,), {'speak': speak})
T.speak(123123)
print(T)
"""
output:
123123
<class '__main__.Myclass'>
"""

可以看到我们通过type方法创建了一个类Myclass,还为其设置了一个类方法speak。而这里类名,父类,类属性都是通过参数传入的,所以这是一个动态创建类的过程。

我们甚至可以进一步的把它扩展。我们可以把一个类存在文本文件中,我们通过读取文本文件来创建类。

x = """
@classmethod
def speak(cls, x):
    print(x)
"""
attr_ = dict()
exec(x, globals(), attr_)

T = type('Myclass', (object,), attr_)
T.speak(123123)
print(T)

exec可以动态的执行文本代码,第一个参数是代码,第二个参数需要传入一个变量作用区域(字典),第三个参数是变异的结果存储的结果(一个Mapping类型)。然后把这个结果传给type就创建出来的一个类。通过这种方式,我们甚至可以在线的创建一些类,更新一些操作。

Metaclass

每一个类都会有一个Metaclass,这个Metaclass就是用来创建自身类的实例的,如果没有指明Metaclass,那么这个值默认就是type。这也就是为什么每一个类的实例都是type的对象的原因。

那么,我们是否可以定义属于自己的Metaclass呢?当然是可以的,只需要继承type类即可。作为Metaclass,其负责的内容是创建类对象,所以我们需要重写__new__方法。例如:

class MyMetaclass(type):
    @classmethod
    def __new__(mcs, *args, **keywords):
        return super().__new__(*args) # 创建类实例

class Test(metaclass=MyMetaclass):# (使用MyMetaclass作为元类)
    pass

由于这里我们是在创建一个类,所以我们可以搞一些小动作,比如给其添加一些类属性:

class MyMetaclass(type):
    @classmethod
    def __new__(mcs, *args, **keywords):
        class_ = super().__new__(*args)
        class_.meta_type = 'MyMetaclass'
        return class_

class Test(metaclass=MyMetaclass):
    pass

print(Test.meta_type)
"""
MyMetaclass
"""

我们还可以像Metaclass中传入参数,使得在创建类时多一些选择

class MyMetaclass(type):
    @classmethod
    def __new__(mcs, *args, **keywords):
        class_ = super().__new__(*args)
        for key, value in keywords.items():
            setattr(class_, key, value)
        return class_

class Test(metaclass=MyMetaclass, name='test'):
    pass
print(Test.name)

上述的输出结果为test。

ABCMeta

ABCMeta是官方提供的一个抽象方法元类,使用它我们就可以定义类接口。其使用方法如下:

  1. 类Test使用ABCMeta为元类
  2. 使用abstractmethod,abstractstaticmethod等装饰器来装饰Test的成员方法,使其成为抽象方法。

这样拥有抽象方法的Test类就无法实例化,而想要实例化就必须继承且实现对应的类

from abc import ABCMeta, abstractmethod, abstractproperty


class Test(metaclass=ABCMeta):
    @abstractmethod
    def speak(self, x):...

    @abstractproperty
    def name(self):...

Test()
"""
Traceback (most recent call last):
  File "e:\PythonProject\main.py", line 68, in <module>       
    Test()
TypeError: Can't instantiate abstract class Test with abstract methods name, speak
"""

如果想要使用这个函数,就必须继承然后实现,这就使得我们可以强迫使用者实现某些方法。

from abc import ABCMeta, abstractmethod, abstractproperty


class Test(metaclass=ABCMeta):
    @abstractmethod
    def speak(self, x):...

    @abstractproperty
    def name(self):...


class RealTest(Test):
    def speak(self, x):
        print(x)

    @property
    def name(self):
        return 'RealTest'

RealTest()

此时RealTest就可以正常的实例化了,因为所有的抽象方法,属性都已经被实现。

dataclass

dataclass是py提供的一种数据接口,我们可以通过它来定义数据类,我们通过类装饰器的方式来装饰类就可以把类变成数据类

from dataclasses import dataclass, field

@dataclass()
class MyData:
    age: int
    name: str = 9

print(MyData(10))
"""
MyData(age=10, name=9)
"""

数据类内部,我们可写上若干种数据的定义,然后在创建时只需要输入参数就可以创建。
dataclass为我们写好了各种常见的操作,例如__eq__, __rper__, __str__等。

from dataclasses import dataclass, field

@dataclass()
class MyData:
    age: int
    name: str = 9

print(MyData(10) == MyData(10))
"""
True
"""

我们还可以指定这些参数是否可以被二次修改,例如:

from dataclasses import dataclass, field

@dataclass(frozen=True)
class MyData:
    age: int
    name: str = 9

x = MyData(10)
x.age = 100
"""
Traceback (most recent call last):
  File "e:\PythonProject\main.py", line 45, in <module>
    x.age = 100
  File "<string>", line 4, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'age'
"""

此时加了frozen=True参数,所以修改就会报错。

我们还可以隐藏某些我们不想让用户看到的参数,此时使用filed函数。

from dataclasses import dataclass, field

@dataclass
class MyData:
    age: int
    name: str = field(default='Mydata', init=False, hash=True, repr=False)

x = MyData(10)

上述代码使用field函数给了name变量值,同时设置其不能通过对象来赋值,也不能被print打印出来,但是可以参与hash。

除此之外,dataclass还提供了__post__init__操作为初始化之后的操作进行一些后处理

from dataclasses import dataclass, field
@dataclass(order=True) # 设置该Class支持排序
class MyData:
    age: int
    name: str = 9
    other_info: list = field(default=None, init=False, repr=True, hash=True)

    def __post_init__(self): # 后续操作,根据age处理斌生成other_info
        if self.age >= 18:
            self.other_info = "this person has matured"
        else:
            self.other_info = "this person has not matured"


x = MyData(10, 'asd')
y = MyData(20, 'asssd')
print(sorted([y, x])) # 排序MyData
"""
[MyData(age=10, name='asd', other_info='this person has not matured'), MyData(age=20, name='asssd', other_info='this person has matured')]
"""

最后一点就是,dataclass还支持排序操作,按照的排序的优先级是变量的顺序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/44987.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Kafka基础架构与核心概念

Kafka简介 Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;它可以处理消费者在网站中的所有动作流数据。架构特点是分区、多副本、多生产者、多订阅者&#xff0c;性能特点主要是…

VisualStudio如何进行插件开发?

文章目录 0.引言1.工具准备2.创建插件项目&#xff08;VSIX&#xff09;3.自定义VSIX属性4.创建一个command命令5.设置command名称6.编写command功能7.调试插件8.安装插件 0.引言 使用Visual Studio插件可以极大地提升开发效率、提供更好的集成环境、丰富扩展生态系统、方便调试…

python报错:‘unicodeescape‘ codec can‘t decode bytes解决办法

参考:https://blog.csdn.net/shuyudexiaowu/article/details/108771481 我的代码是这样&#xff1a; 错误原因是&#xff1a;python把字符串中的反斜杠“ \ ”当成了字符串的一部分&#xff0c;而不是反斜杠。 解决办法两个&#xff1a; 1、在文件目录前加个 r&#xff0c;&…

线性神经网路——线性回归随笔【深度学习】【PyTorch】【d2l】

文章目录 3.1、线性回归3.1.1、PyTorch 从零实现线性回归3.1.2、简单实现线性回归 3.1、线性回归 线性回归是显式解&#xff0c;深度学习中绝大多数遇到的都是隐式解。 3.1.1、PyTorch 从零实现线性回归 %matplotlib inline import random import torch #d2l库中的torch模块&a…

PCL 计算点云AABB包围盒

目录 一、算法原理二、代码实现1、直接计算2、惯性矩法三、结果展示本文由CSDN点云侠原创。爬虫自重,把自己当个人。 一、算法原理 AABB包围盒又称了 轴对齐包围盒,是点云包围盒里最简单的一种,其计算方法也极其简单,看代码即可理解!!!目前PCL中有直接计算和基于惯性偏…

Xshell使用sftp传输文件

单击工具栏新建回话图标&#xff0c;在弹出的新建回话窗口中协议选择SFTP&#xff0c;输入主机名或ip地址&#xff0c;端口号22&#xff0c;单击连接&#xff0c;输入用户名和密码完成创建连接。 本地/远程目录设置&#xff1a;新建会话时在下图中SFTP中设置文件上传下载的本地…

TOOD Task-aligned One-stage Object Detection 论文学习

1. 解决了什么问题&#xff1f; 目标检测通过多任务学习的方式&#xff0c;协同优化目标的分类和定位。分类任务会学习目标的判别特征&#xff0c;关注于目标的显著性或关键区域&#xff0c;而定位任务则学习准确地定位目标的边界。因为定位和分类的学习机制不同&#xff0c;这…

DP学习第三篇之不同路径

DP学习第三篇之不同路径 62. 不同路径 - 力扣&#xff08;LeetCode&#xff09; 一.题目解析 二. 算法原理 状态表示 tips: 经验题目要求。以[i,j]位置为结尾&#xff0c;。。。 dp[i][j]: 走到[i, j]位置时&#xff0c;一共多少种路径 状态转移方程 tips: 用之前或之后的状…

Visual Studio Code安装详细教程

win电脑可以打开该网址 vs官方下载网站 点击这里免费下载 下载下来是一个安装程序&#xff0c;直接以管理员身份运行即可 我同意安装&#xff0c;然后选择D盘的一个空间进行安装 然后点击下一步 安装如图所示勾选&#xff0c;点击下一步 点击安装 等待安装完成即可 打开…

Electron 学习_BrowserWindow

BrowserWindow创建并控制浏览器窗口(主进程) 条件&#xff1a;在 app 模块 emitted ready 事件之前&#xff0c;您不能使用此模块。 1.在加载页面时&#xff0c;渲染进程第一次完成绘制时&#xff0c;如果窗口还没有被显示&#xff0c;渲染进程会发出 ready-to-show 事件 。 在…

前端(九)——探索微信小程序、Vue、React和Uniapp生命周期

&#x1f642;博主&#xff1a;小猫娃来啦 &#x1f642;文章核心&#xff1a;探索微信小程序、Vue、React和Uniapp生命周期 文章目录 微信小程序、Vue、React和Uniapp的基本定义和应用领域微信小程序生命周期生命周期概述页面生命周期应用生命周期组件和API的生命周期钩子 Vu…

失去中国市场的三星继续称霸全球,中国手机的份额反而进一步下降了

市调机构canalys公布了二季度全球手机市场的数据&#xff0c;数据显示三星、苹果的市场份额保持稳定并位居全球前二&#xff0c;三星的表现显然让人称奇&#xff0c;一直被唱衰&#xff0c;却一直都稳稳占据全球手机市场第一名。 从Canalys公布的数据可以看到&#xff0c;三星以…

从零开始学习自动驾驶路径规划-环境配置

从零开始学习自动驾驶路径规划-环境配置 前面&#xff0c;每个人遇到的问题不一样&#xff0c;这里记录了配置步骤和目前遇到的问题&#xff0c;会持续更新报错解决方法。配置时有报错请认真看报错经验 环境配置步骤&#xff08;18.04和20.04都可以&#xff0c;有些问题没遇到…

vue中重新获取数据导致页面加长,要求在页面更新之后浏览器滚动条滚动到之前浏览记录的位置。以及获取当前页面中是哪个元素产生滚动条的方法。

目前的页面样式为&#xff1a; 代码是&#xff1a; <section id"detailSection"><el-tableref"multipleTable":data"logDetailList"style"width: 650px;margin:20px auto;"id"dialogDetail":show-header"fals…

【NLP】图解变压器(transformer)

一、说明 在这篇文章中&#xff0c;我们将看看 The Transformer——一个利用注意力来提高这些模型训练速度的模型。转换器在特定任务中优于谷歌神经机器翻译模型。然而&#xff0c;最大的好处来自变压器如何适应并行化。事实上&#xff0c;谷歌云建议使用The Transformer作为参…

【Linux后端服务器开发】协议定制(序列化与反序列化)

目录 一、应用层协议概述 二、序列化与反序列化 Protocal.h头文件 Server.h头文件 Client.h头文件 server.cpp源文件 client.cpp源文件 一、应用层协议概述 什么是应用层&#xff1f;我们通过编写程序解决一个个实际问题、满足我们日常需求的网络程序&#xff0c;都是应…

0基础学习VR全景平台篇 第69篇:VR直播-如何设置广告

直播间可以插入轮播广告&#xff0c;并且支持外链跳转&#xff0c;能够有效地提升VR直播活动的转化率。 1、点击&#xff0c;添加广告 2、广告图展现形式分为两种&#xff1a;普通广告和全屏广告&#xff0c;普通广告在非全屏播放的直播间显示&#xff0c;全屏广告在全屏播放的…

【Visual Studio】VS调用tensorflow C++API的配置(无需编译)

1. 首先下载并安装visual studio Visual Studio 2015 安装教程&#xff08;附安装包&#xff09;&#xff0c;按照博客中顺序来就可以 如果在安装过程中提示安装包损失或毁坏&#xff0c;参考VS2015安装过程中安装包丢失或损坏解决办法 卡在哪个搜索文件上就找到哪个文件再继…

【100天精通python】Day15:python 第三方模块和包,模块如何以主程序形式执行

目录 1 常用的第三方模块 2. 第三方模块的安装和使用 2.1 安装第三方模块&#xff1a; 2.2 导入第三方模块&#xff1a; 2.3 使用第三方模块&#xff1a; 3 模块以主程序形式执行 4 python 中的包 4.1 python程序的包结构 4.2 创建包 4.3 python中包的导入和使用 5 …

lama cleaner

这里写自定义目录标题 安装参数包含的额外plugins 安装 conda create --name lamacleaner python3.10 pip install -r requirements.txt pip install gfpgan pip install realesrgan pip install rembg pip install .如果安装本package报错&#xff0c;可以尝试改&#xff1…