iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Python进阶学习之带你探寻Python类的鼻祖-元类
  • 229
分享到

Python进阶学习之带你探寻Python类的鼻祖-元类

2024-04-02 19:04:59 229人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录一、类也是对象二、动态地创建类三、使用 type 创建类四、使用type创建带有属性的类五、使用type创建带有方法的类六、到底什么是元类(终于到主题了)七、metaclass属

python是一门面向对象的语言,所以Python中数字、字符串、列表、集合、字典、函数、类等都是对象。

利用 type() 来查看Python中的各对象类型


In [11]: # 数字

In [12]: type(10)
Out[12]: int

In [13]: type(3.1415926)
Out[13]: float

In [14]: # 字符串

In [15]: type('a')
Out[15]: str

In [16]: type("abc")
Out[16]: str

In [17]: # 列表

In [18]: type(list)
Out[18]: type

In [19]: type([])
Out[19]: list

In [20]: # 集合

In [21]: type(set)
Out[21]: type

In [22]: my_set = {1, 2, 3}

In [23]: type(my_set)
Out[23]: set

In [24]: # 字典

In [25]: type(dict)
Out[25]: type

In [26]: my_dict = {'name': 'hui'}

In [27]: type(my_dict)
Out[27]: dict

In [28]: # 函数

In [29]: def func():
    ...:     pass
    ...:

In [30]: type(func)
Out[30]: function

In [31]: # 类

In [32]: class Foo(object):
    ...:     pass
    ...:

In [33]: type(Foo)
Out[33]: type

In [34]: f = Foo()

In [35]: type(f)
Out[35]: __main__.Foo

In [36]: # type

In [37]: type(type)
Out[37]: type

可以看出

数字 1int类型 的对象

字符串 abcstr类型 的对象

列表、集合、字典是 type类型 的对象,其创建出来的对象才分别属于 list、set、dict 类型

函数 funcfunction类型 的对象

自定义类 Foo 创建出来的对象 fFoo 类型,其类本身 Foo 则是 type类型 的对象。

type 本身都是type类型的对象

一、类也是对象

类就是拥有相等功能和相同的属性的对象的集合

在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在 Python 中这一点仍然成立:


In [1]: class ObjectCreator(object):
   ...:     pass
   ...:

In [2]: my_object = ObjectCreator()

In [3]: print(my_object)
<__main__.ObjectCreator object at 0x0000021257B5A248>

但是,Python中的类还远不止如此。类同样也是一种对象。是的,没错,就是对象。只要你 使用关键字 class,Python解释器在执行的时候就会创建一个对象。

下面的代码段:


>>> class ObjectCreator(object):
…       pass
…

将在内存中创建一个对象,名字就是 ObjectCreator。这个 对象(类对象ObjectCreator)拥有创建对象(实例对象)的能力。但是,它的本质仍然是一个对象,于是乎你可以对它做如下的操作:

1.你可以将它赋值给一个变量

2.你可以拷贝它

3.你可以为它增加属性

4.你可以将它作为函数参数进行传递

如下示例:


In [39]: class ObjectCreator(object):
    ...:     pass
    ...:

In [40]: print(ObjectCreator)
<class '__main__.ObjectCreator'>

In [41]:# 当作参数传递

In [41]: def out(obj):
    ...:     print(obj)
    ...:

In [42]: out(ObjectCreator)
<class '__main__.ObjectCreator'>

In [43]: # hasattr 判断一个类是否有某种属性

In [44]: hasattr(ObjectCreator, 'name')
Out[44]: False

In [45]: # 新增类属性

In [46]: ObjectCreator.name = 'hui'

In [47]: hasattr(ObjectCreator, 'name')
Out[47]: True

In [48]: ObjectCreator.name
Out[48]: 'hui'

In [49]: # 将类赋值给变量

In [50]: obj = ObjectCreator

In [51]: obj()
Out[51]: <__main__.ObjectCreator at 0x212596a7248>

In [52]:

二、动态地创建类

因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。首先,你可以在函数中创建类,使用 class 关键字即可。


def cls_factory(cls_name):
    """
    创建类工厂
    :param: cls_name 创建类的名称
    """
    if cls_name == 'Foo':
        class Foo():
            pass
        return Foo  # 返回的是类,不是类的实例

    elif cls_name == 'Bar':
        class Bar():
            pass
        return Bar

IPython 测验


MyClass = cls_factory('Foo')

In [60]: MyClass
Out[60]: __main__.cls_factory.<locals>.Foo # 函数返回的是类,不是类的实例

In [61]: MyClass()
Out[61]: <__main__.cls_factory.<locals>.Foo at 0x21258b1a9c8>

但这还不够动态,因为你仍然需要自己编写整个类的代码。由于类也是对象,所以它们必须是通过什么东西来生成的才对。

当你使用class关键字时,Python解释器自动创建这个对象。但就和Python中的大多数事情一样,Python仍然提供给你手动处理的方法。

三、使用 type 创建类

type 还有一种完全不同的功能,动态的创建类。

type可以接受一个类的描述作为参数,然后返回一个类。(要知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性)

type 可以像这样工作:

type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

比如下面的代码:


In [63]: class Test:
    ...:     pass
    ...:

In [64]: Test()
Out[64]: <__main__.Test at 0x21258b34048>

In [65]:

可以手动像这样创建:


In [69]:# 使用type定义类

In [69]: Test2 = type('Test2', (), {})

In [70]: Test2()
Out[70]: <__main__.Test2 at 0x21259665808>

我们使用 Test2 作为类名,并且也可以把它当做一个变量来作为类的引用。类和变量是不同的,这里没有任何理由把事情弄的复杂。即 type函数 中第1个实参,也可以叫做其他的名字,这个名字表示类的名字


In [71]: UserCls = type('User', (), {})

In [72]: print(UserCls)
<class '__main__.User'>

In [73]:

使用 help测试这2个类


In [74]: # 用 help 查看 Test类

In [75]: help(Test)
Help on class Test in module __main__:

class Test(builtins.object)
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)


In [76]: # 用 help 查看 Test2类

In [77]: help(Test2)
Help on class Test2 in module __main__:

class Test2(builtins.object)
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)


In [78]:

四、使用type创建带有属性的类

type 接受一个字典来为类定义属性,因此


Parent = type('Parent', (), {'name': 'hui'})

可以翻译为:


class Parent(object):
	name = 'hui'

并且可以将 Parent 当成一个普通的类一样使用:


In [79]: Parent = type('Parent', (), {'name': 'hui'})

In [80]: print(Parent)
<class '__main__.Parent'>

In [81]: Parent.name
Out[81]: 'hui'

In [82]: p = Parent()

In [83]: p.name
Out[83]: 'hui'

当然,你可以继承这个类,代码如下:


class Child1(Parent):
    name = 'jack'
    sex =  '男'
    
class Child2(Parent):
    name = 'mary'
    sex = '女'

就可以写成:


Child1 = type('Child1', (Parent, ), {'name': 'jack', 'sex': '男'})

In [85]: Child2 = type('Child2', (Parent, ), {'name': 'mary', 'sex': '女'})

In [87]: Child1.name, Child1.sex
Out[87]: ('jack', '男')

In [88]: Child2.name, Child2.sex
Out[88]: ('mary', '女')

注意:

  • type 的第2个参数,元组中是父类的名字,而不是字符串
  • 添加的属性是 类属性,并不是实例属性

五、使用type创建带有方法的类

最终你会希望为你的类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值就可以了。

添加实例方法


Child1 = type('Child1', (Parent, ), {'name': 'jack', 'sex': '男'})

In [85]: Child2 = type('Child2', (Parent, ), {'name': 'mary', 'sex': '女'})

In [87]: Child1.name, Child1.sex
Out[87]: ('jack', '男')

In [88]: Child2.name, Child2.sex
Out[88]: ('mary', '女')

添加静态方法


In [96]: Parent = type('Parent', (), {'name': 'hui'})

In [97]: # 定义静态方法
    
In [98]: @staticmethod
    ...: def test_static():
    ...:     print('static method called...')
    ...:

In [100]: Child4 = type('Child4', (Parent, ), {'name': 'zhangsan', 'test_static': test_static})

In [101]: c4 = Child4()

In [102]: c4.test_static()
static method called...

In [103]: Child4.test_static()
static method called...

添加类方法


In [105]: Parent = type('Parent', (), {'name': 'hui'})

In [106]: # 定义类方法

In [107]: @claSSMethod
     ...: def test_class(cls):
     ...:     print(cls.name)
     ...:

In [108]: Child5 = type('Child5', (Parent, ), {'name': 'lisi', 'test_class': test_class})

In [109]: c5 = Child5()

In [110]: c5.test_class()
lisi

In [111]: Child5.test_class()
lisi

你可以看到,在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字 classPython 在幕后做的事情,就是通过元类来实现的

较为完整的使用 type 创建类的方式:


class Animal(object):
    
    def eat(self):
        print('吃东西')


def dog_eat(self):
    print('喜欢吃骨头')

def cat_eat(self):
    print('喜欢吃鱼')


Dog = type('Dog', (Animal, ), {'tyep': '哺乳类', 'eat': dog_eat})

Cat = type('Cat', (Animal, ), {'tyep': '哺乳类', 'eat': cat_eat})

# ipython 测验
In [125]: animal = Animal()

In [126]: dog = Dog()

In [127]: cat = Cat()

In [128]: animal.eat()
吃东西

In [129]: dog.eat()
喜欢吃骨头

In [130]: cat.eat()
喜欢吃鱼

六、到底什么是元类(终于到主题了)

元类就是用来创建类的【东西】。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。

元类就是用来创建这些类(对象)的,元类就是类的类,你可以这样理解为:


MyClass = MetaClass() # 使用元类创建出一个对象,这个对象称为“类”
my_object = MyClass() # 使用“类”来创建出实例对象

你已经看到了type可以让你像这样做:


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

这是因为函数 type 实际上是一个元类。type 就是 Python在背后用来创建所有类的元类。现在你想知道那为什么 type 会全部采用小写形式而不是 Type 呢?好吧,我猜这是为了和 str 保持一致性,str是用来创建字符串对象的类,而 int 是用来创建整数对象的类。type 就是创建类对象的类。你可以通过检查 __class__ 属性来看到这一点。因此 Python中万物皆对象

现在,对于任何一个 __class____class__ 属性又是什么呢?


In [136]: a = 10

In [137]: b = 'acb'

In [138]: li = [1, 2, 3]

In [139]: a.__class__.__class__
Out[139]: type

In [140]: b.__class__.__class__
Out[140]: type

In [141]: li.__class__.__class__
Out[141]: type

In [142]: li.__class__.__class__.__class__
Out[142]: type

因此,元类就是创建类这种对象的东西。type 就是 Python的内建元类,当然了,你也可以创建自己的元类。

七、metaclass属性

你可以在定义一个类的时候为其添加 __metaclass__ 属性。


class Foo(object):
    __metaclass__ = something…
    ...省略...

如果你这么做了,Python就会用元类来创建类Foo。小心点,这里面有些技巧。你首先写下 class Foo(object),但是类Foo还没有在内存中创建。Python会在类的定义中寻找 __metaclass__ 属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的 type 来创建这个类。


class Foo(Bar):
    pass

Python做了如下的操作:

1.Foo中有 __metaclass__ 这个属性吗?如果有,Python会通过 __metaclass__ 创建一个名字为Foo的类(对象)

2.如果Python没有找到 __metaclass__,它会继续在 Bar(父类) 中寻找 __metaclass__ 属性,并尝试做和前面同样的操作。

3.如果Python在任何父类中都找不到 __metaclass__,它就会在模块层次中去寻找 __metaclass__,并尝试做同样的操作。

4.如果还是找不到 __metaclass__ ,Python就会用内置的 type 来创建这个类对象。

现在的问题就是,你可以在 __metaclass__ 中放置些什么代码呢?

答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化的type都可以。

八、自定义元类

元类的主要目的就是为了当创建类时能够自动地改变类。

假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定 __metaclass__。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。

幸运的是,__metaclass__ 实际上可以被任意调用,它并不需要是一个正式的类。所以,我们这里就先以一个简单的函数作为例子开始。

python2中


# -*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):

    # class_name 会保存类的名字 Foo
    # class_parents 会保存类的父类 object
    # class_attr 会以字典的方式保存所有的类属性

    # 遍历属性字典,把不是__开头的属性名字变为大写
    new_attr = {}
    for name, value in class_attr.items():
        if not name.startswith("__"):
            new_attr[name.upper()] = value

    # 调用type来创建一个类
    return type(class_name, class_parents, new_attr)

class Foo(object):
    __metaclass__ = upper_attr # 设置Foo类的元类为upper_attr
    bar = 'bip'

print(hasattr(Foo, 'bar'))
# Flase
print(hasattr(Foo, 'BAR'))
# True

f = Foo()
print(f.BAR)

python3中


# -*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):

    #遍历属性字典,把不是__开头的属性名字变为大写
    new_attr = {}
    for name,value in class_attr.items():
        if not name.startswith("__"):
            new_attr[name.upper()] = value

    #调用type来创建一个类
    return type(class_name, class_parents, new_attr)

# 再类的继承()中使用metaclass
class Foo(object, metaclass=upper_attr):
    bar = 'bip'

print(hasattr(Foo, 'bar'))
# Flase
print(hasattr(Foo, 'BAR'))
# True

f = Foo()
print(f.BAR)

再做一次,这一次用一个真正的 class 来当做元类。


class UpperAttrMetaClass(type):
    
    def __new__(cls, class_name, class_parents, class_attr):
        # 遍历属性字典,把不是__开头的属性名字变为大写
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith("__"):
                new_attr[name.upper()] = value

        # 方法1:通过'type'来做类对象的创建
        return type(class_name, class_parents, new_attr)

        # 方法2:复用type.__new__方法
        # 这就是基本的OOP编程,没什么魔法
        # return type.__new__(cls, class_name, class_parents, new_attr)

        
# python3的用法
class Foo(object, metaclass=UpperAttrMetaClass):
    bar = 'bip'

# python2的用法
class Foo(object):
	__metaclass__ = UpperAttrMetaClass
    bar = 'bip'


print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出: True

f = Foo()
print(f.BAR)
# 输出: 'bip'


__new__ 是在__init__之前被调用的特殊方法
__new__是用来创建对象并返回之的方法
而__init__只是用来将传入的参数初始化给对象
这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__

就是这样,除此之外,关于元类真的没有别的可说的了。但就元类本身而言,它们其实是很简单的:

1.拦截类的创建

2.修改类

3.返回修改之后的类

总结

现在回到我们的大主题上来,究竟是为什么你会去使用这样一种容易出错且晦涩的特性?

好吧,一般来说,你根本就用不上它:

“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。” —— Python界的领袖 Tim Peters

到此这篇关于Python进阶学习之带你探寻Python类的鼻祖-元类的文章就介绍到这了,更多相关Python元类内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Python进阶学习之带你探寻Python类的鼻祖-元类

本文链接: https://www.lsjlt.com/news/125407.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Python进阶学习之带你探寻Python类的鼻祖-元类
    目录一、类也是对象二、动态地创建类三、使用 type 创建类四、使用type创建带有属性的类五、使用type创建带有方法的类六、到底什么是元类(终于到主题了)七、metaclass属...
    99+
    2024-04-02
  • Python进阶学习之你真的懂元组吗?
    目录元组作为没有字段名的记录元组拆包具名元组总结元组作为没有字段名的记录 元组中的每个元素其实都存储了该元素的未知以及字段的数据,但是当我们把它理解为”不可变的列表&ld...
    99+
    2023-05-16
    Python元组应用 Python元组
  • 一篇文章带你学习python的函数与类
    目录函数模块类根据类创建实例继承总结现在做的一个小项目需要用到python的相关知识,但是因为太久没用一些东西都忘掉了,因此在本篇博客中记录一下python的函数和类的基础知识,下次...
    99+
    2024-04-02
  • Python进阶语法之类的继承
    目录一、子类的方法__init__( )创建一个Car类创建子类ElelctricCar二、给子类定义属性和方法三、重写父类的方法总结 在编写类时,我们并非每次都需要从空白开始。 如...
    99+
    2024-04-02
  • Python 封装与抽象类的进阶之路
    封装是指将数据和方法绑定在一起,创建一个独立的实体(对象)。通过封装,可以将对象的内部状态隐藏起来,只对外暴露必要的接口。 访问修饰符: public:公共的,可以在任何地方访问 protected:受保护的,只能在类及其子类中访问...
    99+
    2024-04-02
  • Python面向对象编程之类的进阶
    目录1、引用的概念2、对象的拷贝2.1 实例方法的引用2.2 类的特性装饰器3、类的名称修饰3.1 _单下划线开头的名称修饰3.2 _单下划线结尾的名称修饰3.3 __双下划线开头的...
    99+
    2024-04-02
  • Python基础知识学习之类的继承
    目录一、继承的格式二、单继承三、多继承四、重写父类方法五、打印继承关系六、调用父类方法七、私有属性与方法一、继承的格式 类的继承格式如下,括号中的为父类名。 class 类名(父...
    99+
    2024-04-02
  • Python入门学习之类的相关知识总结
    目录前言一、类的定义和使用二、类的方法三、类的属性四、类中常用特殊方法前言 Python是面向对象的程序设计(Object Oriented Programming)。 面向对象的程...
    99+
    2024-04-02
  • Python学习笔记之字典,元组,布尔类型和读写文件
    目录1.字典dict1.1列表和字典的区别1.2字典示例1.3练习:写中国省份与省份缩写对应的字母代码2.元组tuple3.布尔类型bool4.读写文件4.1用命令做一个编辑器4.2...
    99+
    2024-04-02
  • Python学习之装饰器与类的装饰器详解
    目录装饰器装饰器的定义装饰器的用法类中的装饰器类的装饰器 - classmethod类的装饰器 - staticmethod类的装饰器 - property通过学习装饰器可以让我们更...
    99+
    2024-04-02
  • Python学习之不同数据类型间的转换总结
    目录字符串与数字类型的转换字符串与数字之间转换的要求字符串与数字之间的转换函数字符串与列表之间的转换split()函数-字符串转列表join()函数-列表转字符串数据类型转换-小练习...
    99+
    2024-04-02
  • python深度学习tensorflow训练好的模型进行图像分类
    目录正文随机找一张图片读取图片进行分类识别最后输出正文 谷歌在大型图像数据库ImageNet上训练好了一个Inception-v3模型,这个模型我们可以直接用来进来图像分类。 下载链...
    99+
    2024-04-02
  • Python机器学习应用之基于LightGBM的分类预测篇解读
    目录一、Introduction1 LightGBM的优点2 LightGBM的缺点二、实现过程1 数据集介绍2 Coding三、KeysLightGBM的重要参数基本参数调整针对训...
    99+
    2024-04-02
  • Python机器学习应用之支持向量机的分类预测篇
    目录1、Question2、Answer!——SVM3、软间隔4、超平面支持向量机常用于数据分类,也可以用于数据的回归预测 1、Question 我们经常会遇...
    99+
    2024-04-02
  • 剖析 Python 变量与数据类型:深入浅出的学习之旅
    变量是计算机程序中存储数据的容器,其类型决定了存储数据的格式和操作。在 Python 中,变量和数据类型密不可分,深入理解两者对于有效利用语言至关重要。 Python 变量 标识符:变量名称,由字母、数字或下划线组成,不能以数字开头。 ...
    99+
    2024-04-02
  • Python机器学习应用之基于决策树算法的分类预测篇
    目录一、决策树的特点 1.优点 2.缺点 二、决策树的适用场景 三、demo一、决策树的特点 1.优点 具有很好的解释性,模型可以生成可以理解的规则。可以发现特征的重要程度。模型...
    99+
    2024-04-02
  • Python机器学习应用之基于天气数据集的XGBoost分类篇解读
    目录一、XGBoost1 XGBoost的优点2 XGBoost的缺点二、实现过程1 数据集2 实现三、KeysXGBoost的重要参数一、XGBoost XGBoost并不是一种模...
    99+
    2024-04-02
  • Python机器学习应用之基于线性判别模型的分类篇详解
    目录一、Introduction1 LDA的优点2 LDA的缺点3 LDA在模式识别领域与自然语言处理领域的区别二、Demo三、基于LDA 手写数字的分类四、小结一、Introduc...
    99+
    2024-04-02
  • 你真的掌握了Python编程算法学习笔记中的所有数据类型吗?
    Python是一种广泛使用的编程语言,它有很多强大的数据类型,包括数字、字符串、列表、元组、字典等等。在学习Python编程算法的过程中,我们会遇到很多与数据类型相关的问题。本文将帮助你回顾和加深对Python数据类型的理解,让你更好地掌握...
    99+
    2023-07-10
    编程算法 学习笔记 数据类型
  • Python编程算法学习笔记中的数据类型:你需要知道的所有内容。
    Python编程算法学习笔记中的数据类型:你需要知道的所有内容 Python是一种高级编程语言,它有很多内置数据类型,包括数字、字符串、列表、元组、字典等等。在本文中,我们将深入探讨Python中的数据类型及其用法。 数字 在Python中...
    99+
    2023-07-11
    编程算法 学习笔记 数据类型
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作