python元编程详解

news2024/10/5 13:36:42

什么是元编程

软件开发中很重要的一条原则就是“不要重复自己的工作(Don’t repeat youself)”,也就是说当我们需要复制粘贴代码时候,通常都需要寻找一个更加优雅的解决方案,在python中,这类问题常常会归类为“元编程”

元编程目的

是创建函数和类,并用他们操作代码(例如修改,生成,或者包装自己已有的代码)。尽可能的使代码优雅简洁。具体而言,通过编程的方法,在更高的抽象层次上对一种层次的抽象的特性进行修改

元编程应用

给函数添加一个包装(装饰器)

注意:对wraps装饰器的使用进行补充说明,在类装饰器中使用闭包会导致生成的对象不再是被装饰的类的实例,而是在装饰器函数创建的子类的实例,这会影响__name__和__doc__等属性,在上篇我们使用@wraps装饰器对函数装饰器进行操作让问题得到解决,但在类装饰器中这一方法无效。

元类

在理解元类之前,您需要掌握Python中的类。Python对于从Smalltalk语言借用的类是非常奇怪的。在大多数语言中,类只是描述如何生成对象的代码片段。在Python中也是如此:

1

2

3

4

5

6

7

>>> class ObjectCreator(object):

...       pass

...

>>> my_object = ObjectCreator()

>>> print(my_object)

<__main__.ObjectCreator object at 0x8974f2c>

  

一旦使用关键字class,Python就会执行它并创建一个OBJECT。指示

1

2

3

>>> class ObjectCreator(object):

...       pass

...

  在内存中创建一个名为“ObjectCreator”的对象。这个对象(类)本身能够创建对象(实例),这就是为什么它是一个类。但是,它仍然是一个对象,因此:

    1. 您可以将其分配给变量
    2. 你可以复制它
    3. 你可以添加属性
    4. 您可以将其作为函数参数传递

例如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

>>> print(ObjectCreator) # you can print a class because it's an object

<class '__main__.ObjectCreator'>

>>> def echo(o):

...       print(o)

...

>>> echo(ObjectCreator) # you can pass a class as a parameter

<class '__main__.ObjectCreator'>

>>> print(hasattr(ObjectCreator, 'new_attribute'))

False

>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class

>>> print(hasattr(ObjectCreator, 'new_attribute'))

True

>>> print(ObjectCreator.new_attribute)

foo

>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable

>>> print(ObjectCreatorMirror.new_attribute)

foo

>>> print(ObjectCreatorMirror())

<__main__.ObjectCreator object at 0x8997b4c>

  动态创建类

由于类是对象,因此您可以像任何对象一样动态创建它们。首先,您可以使用class以下命令在函数中创建类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

>>> def choose_class(name):

...     if name == 'foo':

...         class Foo(object):

...             pass

...         return Foo # return the class, not an instance

...     else:

...         class Bar(object):

...             pass

...         return Bar

...

>>> MyClass = choose_class('foo')

>>> print(MyClass) # the function returns a class, not an instance

<class '__main__.Foo'>

>>> print(MyClass()) # you can create an object from this class

<__main__.Foo object at 0x89c6d4c>

  

但它还不是我们想要的,创建类应该有更优的方法,由于类是对象,因此它们必须由某些东西生成。

使用class关键字时,Python会自动创建此对象。但与Python中的大多数内容一样,它为您提供了手动执行此操作的方法。我们可用通过type函数查看对象的类型:

1

2

3

4

5

6

7

8

>>> print(type(1))

<type 'int'>

>>> print(type("1"))

<type 'str'>

>>> print(type(ObjectCreator))

<type 'type'>

>>> print(type(ObjectCreator()))

<class '__main__.ObjectCreator'>

  

type除了可以查看数据类型外,还有一个特殊的能力,它也可以动态创建类。type可以将类的描述作为参数,并返回一个类。查看type内部原理

1

2

3

type(name of the class,

     tuple of the parent class (for inheritance, can be empty),

     dictionary containing attributes names and values)

  

例如:

1

2

>>> class MyShinyClass(object):

...       pass

  

可以通过以下方式手动创建:

1

2

3

4

5

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object

>>> print(MyShinyClass)

<class '__main__.MyShinyClass'>

>>> print(MyShinyClass()) # create an instance with the class

<__main__.MyShinyClass object at 0x8997cec>

  

type接受字典来定义类的属性。所以:

1

2

>>> class Foo(object):

...       bar = True

  

可以翻译成:

1

>>> Foo = type('Foo', (), {'bar':True})

  

并用作普通类:

1

2

3

4

5

6

7

8

9

>>> print(Foo)

<class '__main__.Foo'>

>>> print(Foo.bar)

True

>>> f = Foo()

>>> print(f)

<__main__.Foo object at 0x8a9b84c>

>>> print(f.bar)

True

  

当然,你可以继承它,所以:

1

2

>>>   class FooChild(Foo):

...         pass

  

会解释成:

1

2

3

4

5

>>> FooChild = type('FooChild', (Foo,), {})

>>> print(FooChild)

<class '__main__.FooChild'>

>>> print(FooChild.bar) # bar is inherited from Foo

True

  

最后,如果想要为我们创建的类添加方法,只需使用正确的签名定义函数并将其指定为属性即可。

1

2

3

4

5

6

7

8

9

10

11

>>> def echo_bar(self):

...       print(self.bar)

...

>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})

>>> hasattr(Foo, 'echo_bar')

False

>>> hasattr(FooChild, 'echo_bar')

True

>>> my_foo = FooChild()

>>> my_foo.echo_bar()

True

  

在动态创建类之后,您可以添加更多方法,就像向正常创建的类对象添加方法一样。

1

2

3

4

5

6

>>> def echo_bar_more(self):

...       print('yet another method')

...

>>> FooChild.echo_bar_more = echo_bar_more

>>> hasattr(FooChild, 'echo_bar_more')

True

  在Python中,类是对象,您可以动态地动态创建类。这是Python在您使用关键字时所执行的操作class,它通过使用元类来实现。

什么是元类(终于讲到重点了)

元类是创建类的“类”。我们可以定义类来创建实例,python一切皆对象,类也不列外,它是通过元类来创建。类是创建实例的蓝图,元类是创建类的蓝图。可以很容易地看出,Python类中也需要是第一类对象才能启用此行为。

例如:

1

2

MyClass = MetaClass()

my_object = MyClass()

  通过type来创建:

1

MyClass = type('MyClass', (), {})

  这是因为该函数type实际上是一个元类。type是Python用于在幕后创建所有类的元类。

为什么是小写type而不是大学Type?

  type与str创建字符串对象int的类创建整数对象的类类似,它也只是创建类对象的类。我们通过检查__class__属性来查看。

一切,一切,一切重要的事情说三遍,都是Python中的一个对象。这包括整数,字符串,函数和类。所有这些都是对象。所有这些都是从一个类创建的:

1

2

3

4

5

6

7

8

9

10

11

12

13

>>> age = 35

>>> age.__class__

<type 'int'>

>>> name = 'bob'

>>> name.__class__

<type 'str'>

>>> def foo(): pass

>>> foo.__class__

<type 'function'>

>>> class Bar(object): pass

>>> b = Bar()

>>> b.__class__

<class '__main__.Bar'>

  那么__class____class__

1

2

3

4

5

6

7

8

>>> age.__class__.__class__

<type 'type'>

>>> name.__class__.__class__

<type 'type'>

>>> foo.__class__.__class__

<type 'type'>

>>> b.__class__.__class__

<type 'type'>

  

因此,元类只是创建类对象的东西。我们也称它为类工厂

type 是Python使用的内置元类,我们也可以创建自己的元类。

__mataClass__属性

在Python 2中,我们在编写类时添加属性:

1

2

3

class Foo(object):

    __metaclass__ = something...

    [...]

  如果引用__mataClass__属性,python将使用元类类创建Foo,但是这样class Foo(object),类对象Foo并不是在内存中创建的

Python将会在父类中查找__metaclass__,如果没有,就继续向父类的父类查找,如果还是没有,就在模块中找,还是没有的话就用缺省的MetaClass即type创建类。

当你这样做时:

1

2

class Foo(Bar):

    pass

  

Python会执行以下操作:

如果有__metaclass__属性,将会在内存中创建一个类对象,名称Foo使用是__metaclass__。如果Python找不到__metaclass__,它将__metaclass__在MODULE级别查找,并尝试执行相同的操作(但仅适用于不继承任何内容的类,基本上是旧式类)。

如果还是找不到__metaclass__,它将使用Bar's(第一个父级)自己的元类(可能是默认的type)来创建类对象。

Python中的元类3

在Python 3中更改了设置元类的语法:

1

2

class Foo(object, metaclass=something):

    ...

  

__metaclass__不再使用该属性,而是支持基类列表中的关键字参数。但是并不会影响元类的功能。

python3中我们可以将属性作为关键字参数传递给元类,如下所示:

1

2

class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):

    ...

  自定义元类

一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类

自定义元类的主要目的是:

    1. 拦截类的创建
    2. 读取类的信息,可以做修改
    3. 返回新的类

通过传入不同的字符串动态的创建不同的类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

def create_class(name):

    if name == 'user':

        class User:

            def __str__(self):

                return "user"

        return User

    elif name == "company":

        class Company:

            def __str__(self):

                return "company"

        return Company

if __name__ == '__main__':

    Myclass = create_class("user")

    my_obj = Myclass()

    print(my_obj)    #user

    print(type(my_obj))     #<class '__main__.create_class.<locals>.User'>

  

用type创建

1

2

3

4

5

6

7

8

9

10

11

12

13

14

# 一个简单type创建类的例子

#type(object_or_name, bases, dict)

#type里面有三个参数,第一个类名,第二个基类名,第三个是属性

User = type("User",(),{"name":"derek"})

my_obj = User()

print(my_obj.name)    #derek#带方法的创建<br><br>def say(self):     #必须加self

    return "i am derek"

User = type("User",(),{"name":"derek","say":say})

my_obj = User()

print(my_obj.name)     #derek

print(my_obj.say())    #i am derek

  

  

让type创建的类继承一个基类

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

def say(self):     #必须加self

    return "i am derek"

class BaseClass:

    def answer(self):

        return "i am baseclass"

#type里面有三个参数,第一个类名,第二个基类名,第三个是属性

User = type("User",(BaseClass,),{"name":"derek","say":say})

if __name__ == '__main__':

    my_obj = User()

    print(my_obj.name)          #d erek

    print(my_obj.say())         # i am derek

    print(my_obj.answer())      # i am baseclass

  但是在实际编码中,我们一般不直接用type去创建类,而是用元类的写法,自定义一个元类metaclass去创建

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

# 把User类创建的过程委托给元类去做,这样代码的分离性比较好

class MetaClass(type):

    def __new__(cls, *args, **kwargs):

        return super().__new__(cls,*args, **kwargs)

class User(metaclass=MetaClass):

    def __init__(self,name):

        self.name = name

    def __str__(self):

        return "test"

if __name__ == '__main__':

    #python中类的实例化过程,会首先寻找metaclass,通过metaclass去创建User类

    my_obj = User(name="derek")

    print(my_obj)    #test

  

还有一个典型的自定义元类例子就是Django ORM。

元类的主要用例是创建API。

它允许您定义如下内容:

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

但是如果你这样做:

guy = Person(name='bob', age='35')
print(guy.age)

它不会返回一个IntegerField对象。它将返回一个int,甚至可以直接从数据库中获取它。

因为models.Model定义__metaclass__它会使用一些魔法将Person您刚刚使用简单语句定义的内容转换为数据库字段的复杂sql。

Django通过公开一个简单的API并使用元类,从这个API中重新创建代码来完成幕后的实际工作,从而使复杂的外观变得简单。

 

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

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

相关文章

C++015-C++函数

文章目录C015-C函数函数目标char[]和stringchar[]char*string字符常量与字符串常量字符串的输入题目描述 字符串输出题目描述在线练习&#xff1a;总结C015-C函数 在线练习&#xff1a; http://noi.openjudge.cn/ https://www.luogu.com.cn/ 函数 目标 函数是指一段可以直接被…

SVG实例详解系列(一)(svg概述、位图和矢量图区别(图解)、SVG应用实例)

SVG实例详解系列&#xff08;一&#xff09; (svg概述、位图和矢量图区别&#xff08;图解&#xff09;、SVG应用实例&#xff09; 目录 一、什么是SVG? &#xff08;1&#xff09;、位图和矢量图概念&#xff08;图解&#xff09; &#xff08;2&#xff09;、SVG的小例子…

Flutter入门进阶之旅 -开源Flutter项目

开源Flutter项目 该项目为纯flutter端项目&#xff0c;采用aar方式寄生在原生APP中&#xff0c;作为APP中的一个独立模块 在业务逻辑上做到与原生APP完全隔离&#xff0c;Flutter端开发者&#xff0c;可完全不用关注原生端的业务模块 两端开发彼此业务隔离&#xff0c;缩小了对…

数字IC手撕代码--小米科技(除法器设计)

前言&#xff1a; 本专栏旨在记录高频笔面试手撕代码题&#xff0c;以备数字前端秋招&#xff0c;本专栏所有文章提供原理分析、代码及波形&#xff0c;所有代码均经过本人验证。目录如下&#xff1a;1.数字IC手撕代码-分频器&#xff08;任意偶数分频&#xff09;2.数字IC手撕…

wondows10系统python2.7兼容安装python3.10

假设已安装好python2.7和pyhon3.10。 python命令只需要应用程序改名即可&#xff0c;需要修改的有python.exe和pythonw.exe pip命令麻烦点&#xff0c;需要用改名后的程序名 重新安装&#xff0c;命令如下&#xff1a; python3 -m pip install --upgrade pip --force-reinst…

说说 Pluma 插件管理框架

1. 概述 Pluma 是一个用 C 开发的可用于管理插件的开源架构&#xff0c;其官网地址为&#xff1a;http://pluma-framework.sourceforge.net/。该架构是个轻量级架构&#xff0c;非常易于理解。 Pluma 架构有以下基本概念&#xff1a; 1&#xff09;插件的外在行为体现为一个…

【C++的OpenCV】第七课-OpenCV图像常用操作(四):图像形态学-图像侵蚀和扩散的原理

让我们来深化前边学习的内容前言一、图像形态学是什么&#xff1f;二、侵蚀和扩张的原理2.1 图像的侵蚀2.1.1 概念2.1.2 原理解释2.2 图像的扩张2.2.1 概念2.2.2 原理解释相关链接&#xff1a;【C的OpenCV】第六课-OpenCV图像常用操作&#xff08;三&#xff09;&#xff1a;Op…

RK3568镜像的拆包和打包

文章目录 前言一、window上分包和打包分包打包二、Linux上分包和打包分包打包总结前言 本文记录在win10上利用瑞芯微提供的工具进行分包和打包,同样也有Linux教程 提示:以下是本篇文章正文内容,下面案例可供参考 一、window上分包和打包 分包 window下一般直接利用工具即…

【正点原子FPGA连载】 第十八章基于BRAM的PS和PL的数据交互 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第十八章基于BRA…

在 Flutter 中使用 webview_flutter 4.0 | 基础用法与事件处理

大家好&#xff0c;我是 17。 Flutter WebView 一共写了四篇文章 在 Flutter 中使用 webview_flutter 4.0 | 基础用法与事件处理在 Flutter 中使用 webview_flutter 4.0 | js 交互Flutter WebView 性能优化&#xff0c;让 h5 像原生页面一样优秀&#xff0c;已入选 掘金一周 …

AI绘画进军三次元,有人用它打造赛博女友?(diffusion)

目录0 写在前面1 AI绘画技术飞跃2 效果展示3 环境配置3.1 下载基础模型3.2 更新.NET和模型3.3 下载绘画模型3.4 启动项目3.5 标签配置4 结语0 写在前面 机器学习强基计划聚焦深度和广度&#xff0c;加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理&a…

内存数据库-4-[redis]在ubuntu中离线安装

Ubuntu20.04(linux)离线安装redis 官网redis下载地址 下载安装包redis-6.0.9.tar.gz。 1 下载安装 (1)解压 sudo tar -xzvf redis-6.0.9.tar.gz -C /usr/local/ cd /usr/local/redis-6.0.9/(2)编译 sudo make(3)测试 sudo dpkg -i libtcl8.6_8.6.10dfsg-1_amd64.deb sudo d…

纯x86汇编实现的多线程操作系统实践 - 第七章 AP2的用户进程

AP2用户进程的代码为task2.asm。该用户进程将在界面上显示一个移动的弹球。一旦在界面上点击鼠标左键&#xff0c;弹球就会直接从鼠标点击处重新出现并继续移动。如何在界面上显示出一个持续移动的小球&#xff1f;计算小球将移动到的区域1->保留该区域中将被小球覆盖的点-&…

智慧物联网源码带手机端源码 物联网系统源码

在智慧工厂领域&#xff0c;智慧城市领域&#xff0c;都需要对设备进行监控。比如工厂需要对周围环境温度、湿度、气压、电压&#xff0c;灯的开关进行监控。这时候就需要物联网平台来进行管理。 推荐一个基于java开发的物联网平台&#xff0c;前端HTML带云组态、可接入视频监…

【华为OD机试模拟题】用 C++ 实现 - 网上商城优惠活动(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明网上商城优惠活动题目输入输出备注示例一输入输出说明输入说明输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才…

Android zygote进程启动流程

zygote启动过程中涉及到以下模块&#xff1a; app_processzygote USAPsocketFileDescriptor (FD) AndroidRuntimeAppRuntime &#xff08;定义于app_process模块&#xff0c;继承自AndroidRuntime。&#xff09; init进程启动zygote进程&#xff1a; #init.zygote32_64.rc s…

【华为OD机试模拟题】用 C++ 实现 - 统计匹配的二元组个数(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明统计匹配的二元组个数题目输入输出描述示例一输入输出说明示例二输入输出说明备注Code使用说明 参加华为od机试,一定要注意不要完全背诵代码&

python读写hdfs文件的实用解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。喜欢通过博客创作的方式对所学的知识进行总结与归纳,不仅形成深入且独到的理…

Python学习笔记之环境搭建

Python学习笔记之环境搭建1. 下载Python2. Windows 安装最新Python3. Linux 安装最新PythonPython是一种编程语言&#xff0c;可以让您更快地工作并更有效地集成系统。 您可以学习使用Python&#xff0c;并立即看到生产力的提高和维护成本的降低。 Python是荷兰程序员吉多范罗苏…

使用 OpenCV 进行面部和眼睛检测

OpenCV是构建计算机视觉应用程序的强大工具。计算机视觉中最常见的任务之一是人脸检测&#xff0c;它涉及识别图像或视频中人脸的存在、位置和面部特征。在本文中&#xff0c;我们将学习如何使用 Haar 级联分类器检测图像中的人脸。先决条件在开始之前&#xff0c;你需要在计算…