深入了解python中元类的相关知识


Posted in Python onAugust 29, 2019

类也是对象

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

class ObjectCreator:
  pass  
my_object = ObjectCreator()
print(my_object)
"""
输出结果:
<__main__.ObjectCreator object at 0x037DACD0>
"""

但是,python的类不止于此,类同样也是一种对象。

class ObjectCreator:
  pass

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

  • 给它复制一个变量
  • 拷贝它
  • 给它增加属性
  • 将它作为函数参数传递

示例代码:

class ObjectCreator:
  pass
# 把它赋值给一个变量
a = ObjectCreator
print(a) # <class '__main__.ObjectCreator'>
# 作为函数参数传递
def echo(o):
  print(o)
  
echo(ObjectCreator) # <class '__main__.ObjectCreator'>

动态的创建类

因为类也是对象,所以可以在运行时动态的创建它们,使用class关键字即可。

def choose_class(name):
  if name == 'foo':
    class Foo(object):
      pass
    return Foo   # 返回的是类,不是类的实例
  else:
    class Bar(object):
      pass
    return Bar
MyClass = choose_class("foo")
print(MyClass) # 打印类对象
# 输出结果
<class '__main__.choose_class.<locals>.Foo'>
print(MyClass()) # 打印实例对象
# 输出结果
<__main__.choose_class.<locals>.Foo object at 0x0368CFD0>

使用type创建类

我们知道通过type()可以知道这个对象的类型是什么,他还有一个完全不同的功能,动态的创建类。
type可以接受一个类的描述作为参数,然后返回一个类。

语法:

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

MyClass = type("MyClass",(),{})
print(MyClass)
# 输出结果:
<class '__main__.MyClass'>

使用type创建带属性的类

type 接受一个字典来为类定义属性,如下所示:

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

等价于

class Foo:
  bar = True

使用type创建继承的子类

接着上面的代码,我们已经创建了一个Foo类,现在来创建一个它的子类。

FooChild = type("FooChild",(Foo,),{})
print(FooChild.bar) # # bar属性是由Foo继承而来
# 输出结果:
True

注意:

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

使用type创建带有方法的类

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

添加实例方法

def test_f(self):
  print("添加的实例方法")
Foo = type("Foo",(),{"test_f":test_f})
f = Foo()
f.test_f()
# 输出结果:
添加的实例方法

添加静态方法

@staticmethod
def test_static():
  print("添加的静态方法")
Foo = type("Foo",(),{"test_static":test_static})
Foo.test_static()
Foo.test_static()
# 输出结果:
添加的静态方法

添加类方法

@classmethod
def test_class(cls):
  print("添加的类方法")
Foo = type("Foo",(),{"test_class":test_class})
Foo.test_class()
# 输出的结果:
添加的类方法

什么是元类

元类就是用来创建类的“东西”。元类就是就是用来创建类对象的,元类就是类的类。
可以这样理解:

MyClass =  MetaClass() # 使用元类创建类对象
MyObject = MyClass() # 使用类对象创建实例对象

type函数其实就是元类。type就是在Python在背后创建所有类的元类,可以通过__class __属性来查看,__class __的功能是查看对象所在的类,它可以嵌套使用。

class A:
  pass
print(A.__class__)
a = A()
print(a.__class__)
print(a.__class__.__class__)
# 输出结果:
<class 'type'>
<class '__main__.A'>
<class 'type'>

可以看出,最后对象的类都是type元类。

Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type。

整数:

age = 18
print(age.__class__)
print(age.__class__.__class__)
# 输出结果:
<class 'int'>
<class 'type'>

字符串:

name = "张三"
print(name .__class__)
print(name .__class__.__class__)
# 输出结果:
<class 'str'>
<class 'type'>

函数:

def f():
  pass
print(f.__class__)
print(f.__class__.__class__)
# 输出结果:
<class 'function'>
<class 'type'>

自定义元类

首先的了解一下metaclass属性,用它来指定一个元类,python会在定义的类中寻找metaclass属性,如果没找到,就到它的父类找以此类推。如果找到了,python就会用它来创建类对象,如果实在没有找到就会用内建的type来创建这个类。
metaclass中可以放type或者任何使用到type或者子类化type的东东都可以。

自定义类的主要目的:

  • 拦截类的创建
  • 修改类

使用函数实现一个自定义的元类

功能:把不是__开头的类属性名字变为大写

def upper_attr(future_class_name: str,future_class_parents: tuple,future_class_attr: dict):
  newAttr = {}
  for key,value in future_class_attr.items():
    if not key.startswith("__"):
      newAttr[key.upper()] = value
  return type(future_class_name,future_class_parents,newAttr)
class Foo(metaclass=upper_attr):
  name = "张三"
  age = 18

hasattr(Foo,"name") # 判断是否有该类属性 False
hasattr(Foo,"NAME") # True
hasattr(Foo,"age") # False
hasattr(Foo,"AGE") # True

继承type实现一个自定义元类

功能:同上

class MyMetaClass(type):
  def __new__(cls, class_name: str, class_parents: tuple, class_attr: dict):
    newAttr = {}
    for key, value in class_attr.items():
      if not key.startswith("__"):
        newAttr[key.upper()] = value
    # 方法1:通过'type'来做类对象的创建
    # return type(class_name, class_parents, newAttr)  
    # 方法2:复用type.__new__方法
    # 这就是基本的OOP编程,没什么魔法
    # return type.__new__(cls, class_name, class_parents, newAttr)  
    # 方法3:使用super方法
    return super(MyMetaClass, cls).__new__(cls, class_name, class_parents, newAttr)
class Foo(metaclass=MyMetaClass):
  name = "张三"
  age = 18
hasattr(Foo,"name") # 判断是否有该类属性 False
hasattr(Foo,"NAME") # True
hasattr(Foo,"age") # False
hasattr(Foo,"AGE") # True

效果和上面是一样的。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
详解Python中的__getitem__方法与slice对象的切片操作
Jun 27 Python
Python Flask-web表单使用详解
Nov 18 Python
python 遍历目录(包括子目录)下所有文件的实例
Jul 11 Python
解决项目pycharm能运行,在终端却无法运行的问题
Jan 19 Python
Python实现Linux监控的方法
May 16 Python
django rest framework serializers序列化实例
May 13 Python
使用pytorch实现论文中的unet网络
Jun 24 Python
python统计mysql数据量变化并调用接口告警的示例代码
Sep 21 Python
Python数据模型与Python对象模型的相关总结
Jan 26 Python
使用Python快速打开一个百万行级别的超大Excel文件的方法
Mar 02 Python
python自动计算图像数据集的RGB均值
Jun 18 Python
python opencv将多个图放在一个窗口的实例详解
Feb 28 Python
Django shell调试models输出的SQL语句方法
Aug 29 #Python
python实现文件的分割与合并
Aug 29 #Python
Python配置文件处理的方法教程
Aug 29 #Python
浅谈django url请求与数据库连接池的共享问题
Aug 29 #Python
python 进程的几种创建方式详解
Aug 29 #Python
python 列表推导式使用详解
Aug 29 #Python
django 数据库连接模块解析及简单长连接改造方法
Aug 29 #Python
You might like
php 获取百度的热词数据的代码
2012/02/18 PHP
自写的利用PDO对mysql数据库增删改查操作类
2018/02/19 PHP
PHP基于rabbitmq操作类的生产者和消费者功能示例
2018/06/16 PHP
laravel 解决crontab不执行的问题
2019/10/22 PHP
通过PHP实现用户注册后邮箱验证激活
2020/11/10 PHP
javascript面向对象包装类Class封装类库剖析
2013/01/24 Javascript
node.js中的path.join方法使用说明
2014/12/08 Javascript
jquery实现的省市区三级联动
2015/04/02 Javascript
理解Angular数据双向绑定
2016/01/10 Javascript
JavaScript知识点总结(十)之this关键字
2016/05/31 Javascript
js中的关联数组与普通数组详解
2016/07/27 Javascript
Javascript 高性能之递归,迭代,查表法详解及实例
2017/01/08 Javascript
详解nodejs微信公众号开发——5.素材管理接口
2017/04/11 NodeJs
vue实现图片加载完成前的loading组件方法
2018/02/05 Javascript
vue2.0模拟锚点的实例
2018/03/14 Javascript
浅谈在react中如何实现扫码枪输入
2018/07/04 Javascript
VUE中setTimeout和setInterval自动销毁案例
2020/09/07 Javascript
js前端传json后台接收‘‘被转为quot的问题解决
2020/11/12 Javascript
详解JavaScript 中的批处理和缓存
2020/11/19 Javascript
JavaScript canvas实现跟随鼠标移动小球
2021/02/09 Javascript
[02:10]DOTA2 TI10勇士令状玩法及不朽Ⅰ展示:焕新世界,如你所期
2020/05/29 DOTA
django开发教程之利用缓存文件进行页面缓存的方法
2017/11/10 Python
Python实现自定义顺序、排列写入数据到Excel的方法
2018/04/23 Python
tensorflow saver 保存和恢复指定 tensor的实例讲解
2018/07/26 Python
Python3解释器知识点总结
2019/02/19 Python
Python for循环搭配else常见问题解决
2020/02/11 Python
CSS3 优势以及网页设计师如何使用CSS3技术
2009/07/29 HTML / CSS
HTML5 Canvas中绘制矩形实例
2015/01/01 HTML / CSS
在线吉他课程,学习如何弹吉他:Fender Play
2019/02/28 全球购物
证婚人搞笑证婚词
2014/01/10 职场文书
国旗下演讲稿
2014/05/08 职场文书
临时用工协议书范本
2014/10/29 职场文书
2016寒假假期总结
2015/10/10 职场文书
python脚本框架webpy的url映射详解
2021/11/20 Python
关于JS中的作用域中的问题思考分享
2022/04/06 Javascript
Redis高并发缓存架构性能优化
2022/05/15 Redis