零基础学习Python(五)

news2024/11/15 11:19:43

1. 数据描述符与非数据描述符

首先,描述符只能作用于类属性,如果将描述符作用于对象属性,则不会生效。

class D:
    def __get__(self, instance, owner):
        print("~get")

class C:
    def __init__(self):
        self.x = D()

应该将D对象赋值给类C的属性:

class C:
    x = D()

所谓数据描述符,就是实现了__set__或者__delete__魔法方法,如果只实现了__get__魔法方法,就称为非数据描述符。通过给描述符分类,旨在说明数据访问的优先级:数据描述符 -> 对象的属性 -> 非数据描述符 -> 类属性。比如说上面的D的对象就是一个非数据描述符,它的优先级是低于对象属性的,所以如果给对象c的属性x赋新的值,然后查询c.x,不会打印“~get”:

说明没有经过__get__魔法方法拦截,但是如果访问类的属性x,则会被拦截到:

将D改为数据描述符:

class D:
    def __get__(self, instance, owner):
        print("~get")

   def __set__(self, instance, value):
        print("~set")

class C:
    x = D()

此时数据描述符的访问属性最高,对对象属性的访问,全部会被__get__魔法方法拦截(当然对象属性赋值的操作也会被__set__魔法方法拦截):

即使修改c.__dict__字典,仍然无效:

但是对于对象属性的访问,最终是会走__getattribute__魔法方法,因此其优先级最高,默认的__getattribute__魔法方法中就实现了上述优先级,如果重写__getattribute__魔法方法,也就没描述符什么事了:

class C:
    x = D()

    def __getattribute__(self, name):
        print("~aha")

在描述符里操作实例对象的__dict__属性,以修改实例对象的属性:

class D:
    def __set_name__(self, owner, name):
        self.name = name
    
    def __get__(self, instance, owner):
        print("~get")
        return instance.__dict__[self.name]

   def __set__(self, instance, value):
        print("~set")
        instance.__dict__[self.name] = value

class C:
    x = D()

 

2. 类装饰器

上述代码相当于C = report(C),相当于一个oncall函数,创建C的对象,就会调用oncall函数:

 

类装饰器的作用就是类被实例化之前进行一些拦截和干预。

不仅可以使用函数来装饰类,也可以使用类来装饰函数:

class Counter:
    def __init__(self, func):
        self.count = 0
        self.func = func
    
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f'已经被调用了{self.count}次~')
        return self.func(*args, **kwargs)

@Counter
def say_hi():
    print("嗨~")

 

也可以用类装饰类:

class Check:
    def __init__(self, cls):
        self.cls = cls
    
    def __call__(self, *args, **kwargs):
        self.obj = cls(*args, **kwargs)

    def __getattr__(self, name):
        print(f'正在访问{name}')
        return getattr(self.obj, name)

@Check
class C:
    def __init__(self, name):
        self.name = name
    
    def say_hi(self):
        print(f'嗨{self.name}~')

    def say_hey(self):
        print(f'嘿{self.name}~')

上述例子中,c1的name属性被c2给覆盖了。这是因为类装饰器装饰类时,相当于C = Check(C),即C不再是一个类了,而是一个Check对象; c1 = C("c1"),相当于调用check对象的__call__方法,__call__方法里是在创建一个C类的对象,赋给了check对象的属性,而这个check对象是只有一个,即用类装饰器装饰类的时候生成的那一个,因此再次执行c2 = C("c2")相当于覆盖了之前同一个check对象的obj属性;执行c1.name,相当于去寻找check对象的name属性,显然check对象没有name属性,因此去调用__getattr__方法,即去寻找其obj属性(也就是C类对象,这里就是c2)的name属性。本质原因是共用了一个Check对象

为了解决这个问题,将Check类改为:

def report(cls):
    class Check:
        def __init__(self, *args, **kwargs):
            self.obj = cls(*args, **kwargs)

        def __getattr__(self, name):
            print(f'正在访问{name}')
            return getattr(self.obj, name)
    return Check

@report
class C:
    def __init__(self, name):
        self.name = name
    
    def say_hi(self):
        print(f'嗨{self.name}~')

    def say_hey(self):
        print(f'嘿{self.name}~')

上述代码C不再是C类,而是C = report(C),是一个Check类,执行c1 = C("c1")相当于生成一个check对象,对象的obj属性是一个C类的对象,里面存了name属性。同理执行c2 = C("c2")相当于生成另一个check对象,同样obj属性保存了另一个C类的对象,里面存了name属性。此时Check类对象不再只只有一个,而是有两个,可以访问到各自的name属性,不会被覆盖。

3. type函数

type函数的常用的用法:

type函数的一些不常用的用法:

本质上就是因为type函数返回对象所属的类。那如果type函数传入的就是一个类,而不是一个对象,会发生什么?答案是会返回type类:

这是为什么?因为Python中万物皆对象,一个类也是对象,类是由type类衍生出来的对象。因type函数隐藏的一个更强大的功能就是创造类:

C = type("C", (), {})

上面的代码是用type函数创建了一个类,第一个参数是类名,第二个参数是父类,参数类型是元祖,第三个参数是类的属性和方法。以上代码与以下代码等同: 

class C:
    pass

 

使用type函数创建D类,继承C类

 

创建带有属性的类:

 

创建带有方法的类:

def funC(self, name):
    print(f'Hello {name}')

F = type(F, (), dict(say_hi=funC))

type函数还有第四个参数,用于给__init_subclass__魔法方法传递参数。首先看__init__subclass__魔法方法的作用:用于覆盖子类的类属性

class C:
    def __init_subclass__(cls, value):
        print("父爱如山~")
        cls.x = value

class D(C, value=250):
    x = 520

 一旦定义完类D,类C的__init_subclass__魔法方法就会被调用,打印“父爱如山~”,并且D的类属性x的值为520:

上述代码用type函数实现:

D = type("D", (C,), dict(x=250), value=520)

4. 元类

简单来说,继承type的类就是元类:

class MetaType(type):
    pass

class C(metaclass=MetaType):
    pass

 

可哟看出,类C的类型:type(C)是我们刚写的元类MetaType,不再是type了。而MetaType的类型是type:

元类相当于创造类的模板,在用元类创造普通类的过程中,一旦普通类定义完成,其元类的__new__和__init__魔法方法就会被调用,但是在创建普通类的对象的过程中不会被调用:

由此可见,普通类相当于人,元类相当于神,而type则是众神之神。元类中的__new__方法的各参数含义:mcls——元类,name——普通类名,bases——普通类继承的父类,attrs——普通类的属性,__init__方法的各参数含义:cls——普通类,name——普通类名,bases——普通类继承的父类,attrs——普通类的属性:

如果在元类中定义__call__魔法方法,那么在普通类实例化对象的过程中就会拦截:

class MetaType(type):
    def __call__(self, *args, **kwargs):
        print("__call() in MetaC~")

class C(metaclass=MetaType):
    pass

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

72.树形列表绑定对应的右键菜单

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于:易道云信息技术研究院 上一个内容:71.对象数据存储设计 以 71.对象数据存储设计 它的代码为基础进行修改 效果图&#xf…

后端常见问题及深度解决方案

🐟作者简介:一名大三在校生,喜欢编程🪴 🐡🐙个人主页🥇:Aic山鱼 🐠WeChat:z7010cyy 🦈系列专栏:🏞️ 前端-JS基础专栏✨前…

linux脚本: 检测多个服务端口是否处于侦听状态或监听状态

目录 一、背景 1、系统监控和健康检查 2、安全性评估 3、故障排查 4、合规性检查 5、资源管理 6、服务依赖性检查 二、需求和分析 1、需求 2、分析 三、脚本文件 1. 创建脚本文件 2. 编写脚本代码 3、代码解释 (1)定义要检查的端口列表 …

视觉全能!自回归要反超扩散?Lumina-mGPT:任意分辨率丝滑逼真图像生成(上海AI Lab)

文章链接:https://arxiv.org/pdf/2408.02657 git链接:https://github.com/Alpha-VLLM/Lumina-mGPT 亮点直击 通过多模态生成预训练的自回归Transformer,而不是从头训练,可以作为逼真的文本到图像生成和视觉与语言任务统一的有效初…

Linux shell编程:监控进程CPU使用率并使用 perf 抓取高CPU进程信息

0. 概要 本文将介绍一个用于监控一组进程CPU使用率的Shell脚本,,当检测到某进程的CPU使用率超出阈值时,使用 perf 工具抓取该进程的详细信息。 本shell脚本为了能在普通嵌入式系统上运行做了妥协和优化。 1. shell脚本流程的简要图示&#…

Spring 中请求作用域的数据存储在 ThreadLocal 中还是 Spring 容器中?

微信中阅读,欢迎👏👏👏关注公众号:CodeFit 。 创作不易,如果你觉得这篇文章对您有帮助,请不要忘了 点赞、分享 和 关注,为我的 持续创作 提供 动力! 最近看到一个有趣的问题,Request Scope(请求作用域) 的数据是存储在 ThreadLocal 中,还是 Spring 容器中? 事…

前端(六):Vue组件库Element

一、引入 Element:是饿了吗团队研发,一套为开发者、设计师和产品经理准备的基于vue2.0的桌面端组件库。组件:组成网页的部件,例如超链接、按钮、图片、表格、表单、分页条等。官网:https://element.eleme.cn/#/zh-CN …

景芯SoC DDR子系统

Memory子系统主要由DDR Controller和DDR_PHY(含DDR_IO)两个部分组成。DDR Controller主要承担其它子系统(如CPU)与Memory子系统进行数据交互时的传输效率及调度,DDR_PHY主要负责数据交互过程中的传输速度。 DDR内存接口IP解决方案包括DDR控制…

Revit二次开发选择过滤器,SelectionFilter

过滤器分为选择过滤器与规则过滤器 规则过滤器可以看我之前写的这一篇文章: Revit二次开发在项目中给链接模型附加过滤器 选择过滤器顾名思义就是可以将选择的构件ID集合传入并加入到视图过滤器中,有一些场景需要对某些构件进行过滤选择,但是没有共同的逻辑规则进行筛选的情况…

健康管理系统

目录 第1章 系统概述 第2章 可行性研究 2.1 项目背景及意义 2.2 可行性研究 第3章 需求分析 3.1 功能性需求 3.2 非功能性需求 3.2.1 性能需求 第4章 总体设计 4.1 技术架构 4.2功能模块设计 第5章 详细设计 5.1 主页 5.2 写剧本杀 5.3 剧本杀分类管理 5.4 个人…

PointNet: Deep Learning on Point Sets for 3D Classification and Segmentation

Abstract 通常情况下研究人员会把点云数据转换为规则的3D体素网格或图像集合。这导致数据不必要的庞大,所以本文引入了一种新型的神经网络,能很好的尊重点云的排列不变性,名称是pointnet,并且能够应用于分类、分割、场景解析等下…

大模型入门无敌!《大模型基础》教材发布,已开源!

浙江大学DAILY实验室毛玉仁研究员、高云君教授领衔撰写的《大模型基础》教材第一版付梓。 本书旨在针对大语言模型感兴趣的读者系统地讲解相关基础知识、介绍前沿技术。作者团队将认真严肃开源社区以及广大专家学者的建议,持续进行月度更新,致力打造易读…

SQLServer Manager Studio扩展开发从入门到弃坑(针对17 ,18 。19)

Visualstudio的已经开发好了 可以在这里找到。 (如果低版本不适用,那么,我还要重新下载老版本vs开发一版) image.png image.png ,可这个就是不行,直接运行点这些按钮加载失败,而我直接不调试模式,则直接什么…

【运维类】智慧运维系统建设方案(PPT原件完整版)

建设方案目录: 1、智慧运维系统建设背景 2、智慧运维系统建设目标 3、智慧运维系统建设内容 4、智慧运维系统建设技术 5、智慧运维系统建设流程 6、智慧运维系统建设收益 企业对运维管理的需求: 1、提高运维效率:降低运维成本,提高…

我定制了一个属于自己的录屏软件

相信很多朋友都用过不同软件的录屏功能,但是这些软件多少还让存在一些缺点,让我们在录屏的时候不能得心应手。 今天我就来手把手教大家自己来制作一个私人订制的浏览器录屏器, 录屏器 我们可以使用浏览器的屏幕捕获API接口来帮助我们来捕获…

java里的序列化反序列化、HttpMessageConverter、Jackson、消息转化器、对象转化器...都是啥?

前段时间在学习SSM框架(spring boot、spring MVC、mybatis)后端项目的时候,发现他们的项目里:响应类Result类要实现Serializable接口、转化响应给前端的时间数据的格式要用到什么“消息转换器”MappingJackson2HttpMwssageConvert…

Vue:Vue3-TypeScript-Pinia-Vite-pnpm / 基础项目 / 20240807

一、项目技术栈 / 依赖 序号技术栈版本解释1node20.14.02vue 3.4.31 3vite 5.3.4 4TypeScript 5.2.2 5 types/node 22.0.2 解决TypeScript项目中缺少对应模块的类型定义文件的问题6 element-plus 2.7.8 ui组建7 types/js-cookie js-cookie 3.0.6 3.0.5 8 sass 1.77.8 9 hu…

zdppy+vue3+onlyoffice开发文档系统实战20240807上课笔记 解决了最近文档页面几个遗留的文档

小技巧 vite配置 open: true 可以自动打开浏览器。 目前 遗留任务 1、在名称前面,渲染这个文档的图标 2、大小的基本的单位是kb,超过1024kb则换成mb,主要是这两个单位 3、数据按照最近访问时间倒序 4、给文件名价格链接,实现和…

C语言深度剖析(部分)--剩下随缘更新

C语言深度剖析 关键字auto-最宽容大度的关键字 变量的分类 代码块:用{ }括起来的区域 局部变量:包含在代码块中的变量,局部变量具有临时性,进入代码块,自动形成局部变量,退出代码块自动释放。 全局变量…

鸿蒙AI功能开发【hiai引擎框架-人脸比对】 基础视觉服务

hiai引擎框架-人脸比对 介绍 本示例展示了使用hiai引擎框架提供的人脸比对能力。 本示例模拟了在应用里,选择两张图片,计算两个图中最大人脸的相似度 需要使用hiai引擎框架人脸比对接口hms.ai.face.faceComparator。 效果预览 使用说明:…