Python的类(Class)和描述器(Descriptor)

1. 背景

笔者的大数据平台XSailboat的SailWorks模块包含离线分析功能。离线分析的后台实现,包含调度引擎、执行引擎、计算引擎和存储引擎。计算和存储引擎由Hive提供,调度引擎和执行引擎由我们自己实现。调度引擎根据DAG图和调度计划,安排执行顺序,监控执行过程。执行引擎接收调度引擎安排的任务,向Yarn申请容器,在容器中执行具体的任务。

在容器中执行的任务我们是用Python语言实现的。在实现这个组建时,笔者是对着python的基础语法教程,边学边写。基本实现离线分析的功能之后,就开始做项目,做实时计算,开发大数据平台的其它功能模块,一转眼已经过去将近两年,现回过头来继续完善离线分析功能,对执行引擎中的python执行组件进一步完善,扩展。为此再进阶一步系统学习一下Python,在最近将写一些Python相关的笔记。

2 . 类(Class)

这里我们不去细究Python底层到底是怎么做的,主要是从现象总结一些规律,以更方便记住。欲看底层逻辑原理,可以看此B站视频【python】你知道定义class背后的机制和原理嘛?当你定义class的时候,python实际运行了什么呢?

先看一下下面的代码:

class B:def __init__(self):print('类型B的对象实例化')class A:f1 = '字段1'f3 = B()def __init__(self):self.f2 = '字段2'# 此处已经输出:类型B的对象实例化
a = A()			# 输出:类型A的对象实例化
a.f1 = '字段1改'
print(a.f1)     # 输出:字段1改
print(A.f1)     # 输出:字段1
print(A.f2)     # 报错,type object 'A' has no attribute 'f2'

从中我们可以总结出以下规律:

  1. class类也是一种对象,成员属性会成为这个类对象的属性。
  2. 类的加载过程中,类对象的属性会被初始化,且先与构造函数__init__被调用。
  3. 实例化对象,它有自己的属性表,在实例化过程中会浅clone当前类对象属性取值。
  4. 实例对象的动态增加的属性,不纳入类属性中。
class A:def __init__(self, name: str):self.name = nameprint(f'类型A的对象实例化{name}')def saiHi(self):print(f'你好,{self.name}')a1 = A('a1')
a2 = type(a1)('a2')		# 使用a1的类型A,构建一个对象
print(A.__dict__)
'''
输出:
{'__module__': '__main__', '__init__': <function A.__init__ at 0x105bce040>, 'saiHi': <function A.saiHi at 0x105bce0d0>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
'''def m0(self, name):self.name = nameprint(f'类型B的对象实例化{name}')def m1(self):print(f'你好,{self.name}')b_class = {'__init__': m0 ,'sayHi': m1
}# 仿照A,动态构建出一个和A功能相同的类型B出来
B = type('B', (), b_class)
b = B('b1')
b.sayHi()

3. 描述器(Descriptor)

官方文档:《描述器使用指南》

  1. descriptor 就是任何一个定义了__get__()、__set__()或__delete__() 的对象。
  2. 可选地,描述器可以具有 set_name() 方法。这仅在描述器需要知道创建它的类或分配给它的类变量名称时使用。(即使该类不是> 描述器,只要此方法存在就会调用。)
  3. 在属性查找期间,描述器由点运算符调用。
  4. 描述器仅在用作类变量时起作用。放入实例时,它们将失效。
  5. 描述器的主要目的是提供一个挂钩,允许存储在类变量中的对象控制在属性查找期间发生的情况。

3.1 第2条实践

class Name:def __set_name__(self, owner, name):print(f'我的参数名是{name}')class A:myNameA1 = Name()				# 输出:我的参数名是myNameA1def __init__(self):self.myNameA1 = Name()		# 没有输出self.myNameA2 = Name()		# 没有输出myName = Name()						# 没有输出

说明__set_name__只有当其在类的构造和初始化过程中,被构造出来赋值给类的成员变量时,此方法才会被调用。

3.2 内部机制探索

class Name:def __get__(self, instance, owner):print(f'self:{self},instance:{instance} , owner:{owner}')return '张三'class A:myName = Name()a = A()
a.myName = '李四'
print(a.myName)			# 1.输出:李四
Name.__set__ = lambda self, instance, value: None
print(a.myName)			# 2.输出:张三
'''
输出:
self:<__main__.Name object at 0x10ce71d30>,instance:<__main__.A object at 0x10ce715b0> , owner:<class '__main__.A'>
张三
'''
A.myName = '王五'
print(a.myName)			# 3.输出:李四
  1. 之所以输出“李四”,是因为a对象,它的成员属性myName被设置成了“李四”,当前a.myName属性就是字符串‘李四’
  2. 在给Name设置了__set__方法之后,之所以输出“张三”,是因为python内部,发现A类的成员属性myName是Name类型,它是一个同时具有__get__和__set__方法的的描述器,它就会去调用描述器对象A.myName的__get__方法。
  3. 之所以会输出‘李四’,是因为此时A.myName是字符串“王五”,它不是一个描述器。所以它就会直接取出a.myName的值字符串“王五”。这也说明了描述器的第4点特性,为什么在实例中构造的描述器对象并被赋值给了实例对象的成员变量,描述器是无法起作用的。

4. 装饰器(decorator)

python的装饰器可以用在函数上(函数装饰器),也可以用在类上(类装饰器)。它的本质是

a = func(a)

即把一个函数或类映射成另一个函数或类(甚至是常量),并且赋值给原来的函数名或类名变量。在python中,函数名或类名,它就是一个指向函数对象或类对象的变量名,所以它可被重新赋值。

python中,装饰器之所以把一个函数或类映射成另一个函数或类之后,又赋值给了原来的函数名或类名变量,目的就是为了让人/代码逻辑无感/无需修改这个函数或类的相关使用代码的前提下,修改函数/类的成员方法的逻辑,修改类的一些特性等。所以经过映射得到的新的函数或类,与被装饰的类,形式上应该是兼容的。

这是一个给函数运行时长计时的例子:

import timedef clock(func):def wrapper(*args, **kwargs):start = time.time()func(*args, *kwargs)print(f'耗时:{time.time() - start}')return wrapper@clock
def test():print('开始干活')time.sleep(2)print('活干完了')test()

装饰器的实现,不仅可以是函数,还可以是实现了__call__方法的类。

4.1 函数和类形式上的等价性

在python中,一切皆对象,函数是对象,类也是对象。看下面的代码:

def f():print("函数做了一些事情")class F:def __init__(self):passdef __call__(self, *args, **kwargs):print("方法做了一些事情")f()				# 输出:函数做了一些事情 
f1 = F()
f1()			# 输出:方法做了一些事情
# 等价于F()()

我们可以看出f()和f1()(即F()())在形式上是一样的。再考虑复杂点,带参数的情况:

class F:def __init__(self, attr):self.attr = attrdef __call__(self, *args, **kwargs):print(f"方法做了一些事情,属性是{self.attr}")def f(attr):def fi():print(f"方法做了一些事情,属性是{attr}")return fiF('python')()			# 方法做了一些事情,属性是python
f('python')()			# 方法做了一些事情,属性是python

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

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

相关文章

【DOCKER】docker 安装sonarque

安装docker 安装docker https://blog.csdn.net/BThinker/article/details/123358697 加入阿里云镜像 https://blog.csdn.net/TommyXu8023/article/details/113291112 { "registry-mirrors": ["https://alzgoonw.mirror.aliyuncs.com"] }安装sonarqube ht…

【LVGL源码移植环境搭建】

LVGL源码移植&环境搭建 ■ LVGL源码移植■ 下载LVGL源码■ 修改LVGL文件夹■■■■ 视频链接 Ubuntu模拟器环境建置 ■ LVGL源码移植 ■ 下载LVGL源码 LVGL源码 我们以选择v8.2.0为例&#xff0c;选择8.2.0下载 ■ 修改LVGL文件夹 1.我们只需要关注这5个文件即可&…

《Docker技术革命:从虚拟机到容器化,全面解析Docker的原理与应用-上篇》

文章目录 Docker为什么会出现总结 Docker的思想Docker历史总结 Docker能干嘛虚拟机技术虚拟机技术的缺点 容器化技术Docker和虚拟机技术的区别 Docker概念Docker的基本组成镜像&#xff08;image)容器&#xff08;container&#xff09;仓科&#xff08;repository&#xff09;…

GitHub工作流的使用笔记

文章目录 前言1. 怎么用2. 怎么写前端案例1&#xff1a;自动打包到新分支前端案例2&#xff1a;自动打包推送到gitee的build分支案例3&#xff1a;暂时略 前言 有些东西真的就是要不断的试错不断地试错才能摸索到一点点&#xff0c;就是摸索到凌晨两三点第二天要8点起床感觉要…

JDK8对List对象根据属性排序

文章目录 JDK8对List对象根据属性排序1. 被排序字段为null或者空时候报错2. 使用Stream流排序2.1 根据name升序2.2 根据name升序&#xff0c;score降序 3. 使用Collections排序3.1 根据name升序3.2 根据name升序&#xff0c;score降序 4. 完整的demo JDK8对List对象根据属性排序…

聊一聊GPT、文心、通义、混元

我使用同一个Prompt提示词“请以记叙文的文体来写”&#xff0c;分别发送给GPT-3.5&#xff08;调用API&#xff09;、文心、通义、混元&#xff0c;下面是它们各自生成的文本内容&#xff0c;大家一看便知了。 GPT-3.5&#xff1a; 在我个人使用GPT模型的过程中&#xff0c;我…

【C语言】学生管理系统

大家好&#xff0c;欢迎来到我的博客总结应用。在上一篇博客中&#xff0c;我写了有关结构体和内存操作函 数的总结&#xff0c;这些博客记录了我的学习、思考和经验。为了更好地总结和回顾这些内容&#xff0c;在此 篇博客中&#xff0c;我编写了”学生管理系统“来帮助我整理…

Facebook的创新征程:社交媒体的演进之路

在当今数字化时代&#xff0c;社交媒体已经成为人们生活中不可或缺的一部分&#xff0c;而Facebook作为社交媒体领域的巨头&#xff0c;一直在不断创新和演进。本文将深入探讨Facebook的创新征程&#xff0c;追溯其社交媒体的发展历程&#xff0c;探讨其对用户、社会和数字时代…

echart 实现自定义地图

先上效果图 需求&#xff1a;自定义区域平面图&#xff0c;支持区域高亮 // 2D详情const initChartsMapItemB async (flow: any, mapbg: any) > {// mapbg 为svg的地址 import mapbg from //assets/json/map/F42d.svgconst svgData (await request.get(mapbg)) as anye…

WPF应用程序(.Net Framework 4.8) 国际化

1、新建两个资源字典文件zh-CN.xaml和en-US.xaml&#xff0c;分别存储中文模板和英文模板 (1) zh-CN.xaml <ResourceDictionary xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml&q…

(delphi11最新学习资料) Object Pascal 学习笔记---第4章第1节(过程和函数)

第4章 过程与函数 ​ Object Pascal 语言&#xff08;以及 C 语言的类似功能&#xff09;强调的另一个重要思想是例程的概念&#xff0c;例程基本上是一系列具有唯一名称的语句&#xff0c;可以多次调用。例程&#xff08;或函数&#xff09;通过名称来调用&#xff0c;这样就…

基于链表实现贪吃蛇游戏

本文中&#xff0c;我们将使用链表和一些Win32 API的知识来实现贪吃蛇小游戏 一、功能 &#xff08;1&#xff09;游戏载入界面 &#xff08;2&#xff09;地图的绘制 &#xff08;3&#xff09;蛇身的移动和变长 &#xff08;4&#xff09;食物的生成 &#xff08;5&…

阿里云盘分享多文件方法

1、单次分享不超过100个文件/文件夹 2、分享的文件夹目录不超过15个子文件夹层级 3、文件夹里面的总文件数量不能超过4000个 其实限制的条件蛮高的&#xff0c;但是对于这么大容量的网盘&#xff0c;稍微一分享就会超过条件限制&#xff0c;有的人会拆分为好几个文件多次分享&a…

CentOS 7 部署 ZeroTier Moon 节点

ZeroTier是一套使用UDP协议构建的SD-WAN网络软件&#xff0c;其主要有三部分组成&#xff1a;行星服务器Planet、月亮服务器Moon、客户端节点LEFA&#xff0c;行星服务器是ZeroTier的根节点&#xff0c;可以采用ZeroTier官方的服务器&#xff0c;也可以使用开源代码自行搭建 月…

基于python的城市旅游数据采集分析系统

基于python的城市旅游数据采集分析系统 "A Python-based City Tourism Data Collection and Analysis System" 下载链接&#xff1a;基于python的城市旅游数据采集分析系统 目录 目录 2 摘要 3 关键词 4 第一章 引言 4 1.1 研究背景 4 1.2 国内外研究现状 5 1.3 研究…

sql注入之union联合注入

一、Union注入 联合查询注入是联合两个表进行注入攻击&#xff0c;使用关键词 union select 对两个表进行联合查询。两个表的字段数要相同&#xff0c;不然会出现报错。列数相同 union 特性是显示两张表 我们就可以吧第一个参数变为------负--的 或者不存在的值 就行了 显示就…

JAVA处理类似饼状图占比和100%问题,采用最大余额法

前言&#xff1a; 在做数据统计报表的时候&#xff0c;有两种方式解决占比总和达不到100%或者超过100%问题。 第一种方式是前端echart图自带的算分框架。 第二种方式是java后端取处理这个问题。 现存问题&#xff1a; 前端通过饼状图的方式去展示各个分类的占比累加和为100%问题…

公司宣传电子画册的制作方法

​制作公司宣传电子画册是一种非常有效的方式&#xff0c;可以展示公司的形象和产品&#xff0c;同时也可以吸引更多的潜在客户。不仅低碳环保&#xff0c;还省了不少人力和财力&#xff0c;只要一个二维码、一个链接就能随时随地访问公司的宣传画册。以下是一些制作电子画册的…

Jmeter学习系列之三:测试计划详细介绍

目录 前言 步骤1:启动JMeter窗口 步骤2:添加/删除测试计划元素 步骤3:加载并保存测试计划元素。 步骤4:配置树元素 步骤5:保存JMeter测试计划 步骤6:运行JMeter测试计划

TSINGSEE青犀视频智慧电梯管理平台,执行精准管理、提升乘梯安全

一、方案背景 随着城市化进程的不断加快&#xff0c;我国已经成为全球最大的电梯生产和消费市场&#xff0c;电梯也成为人们日常生活中不可或缺的一部分。随着电梯数量的激增&#xff0c;电梯老龄化&#xff0c;维保数据不透明&#xff0c;物业管理成本高&#xff0c;政府监管…