Python常见面试题011. 如何在Python中动态创建类?

python,常见,面试题,如何,动态创建 · 浏览次数 : 72

小编点评

**在 Python 中动态创建类** 可以使用 `type()` 函数来动态创建类。 `type()` 函数接受一个或多个参数,其中包含类名、基类、属性和方法的定义。 **参数** * **类名**:要创建的类的名称。 * **基类**:要继承于的类类型。 * **属性和方法**:类中的属性和方法定义。 **示例** ```python # 创建一个名为 "Person" 的类,继承自 "object" 类 class Person(object): def __init__(self, name): self.name = name # 创建一个名为 "wuxianfeng" 的实例 wxf = Person("wuxianfeng") # 打印实例属性的值 print(wxf.name) # 输出 "wuxianfeng" ``` **使用 `type()` 创建类** 可以使用 `type()` 函数创建一个包含类属性的类。例如,以下代码创建一个名为 `C` 的类,其属性名为 `name`: ```python # 创建一个包含类属性的类 C = type('C', (), {'name': str}) ``` **使用 `type()` 创建带继承的类** 可以使用 `type()` 创建一个包含多个基类的类。例如,以下代码创建一个名为 `B` 的类,它继承自 `A` 类: ```python # 创建包含多个基类的类 B = type('B', ('A',)) ``` **使用 `type()` 创建带类属性的类** 可以使用 `type()` 创建一个包含类属性的类,并为属性设置值。例如,以下代码创建一个名为 `C` 的类,其属性名为 `name`,其值为 "wuxianfeng": ```python # 创建包含类属性的类,并为属性设置值 C = type('C', (), {'name': str}) c = C('wuxianfeng') ``` **注意** * `type()` 函数默认会递归调用父类的 `__new__` 方法,以创建子类的类型。 * 如果子类未重载 `__new__` 方法,则父类的类型将被使用。 * `type()` 函数创建的类是一个独立的类型,它可以被其他模块导入。

正文

011. 如何在Python中动态创建类?

说在前面

  • 答案是type

  • 你印象中的type是用来查看对象的类型的

    li = []
    type(li)  # 得到list
    
  • 对自定义的类是这样的

    class Person:
        pass
    
    wuxianfeng = Person()
    type(wuxianfeng)  # __main__.Person
    
  • 此处的wuxianfeng是一个Person类的实例

  • 既然一切皆对象,那么Person的类型是啥呢?

    type(Person)  # type
    

去官网看看

https://docs.python.org/zh-cn/3.9/library/functions.html?highlight=type#type

class type(name, bases, dict, **kwds)

1. 传入一个参数时,返回 object 的类型。 返回值是一个 type 对象,通常与 object.__class__ 所返回的对象相同。
2. 推荐使用 isinstance() 内置函数来检测对象的类型,因为它会考虑子类的情况。

3. 传入三个参数时,返回一个新的 type 对象。 这在本质上是 class 语句的一种动态形式,
	name 字符串即类名并会成为 __name__ 属性;
	bases 元组包含基类并会成为 __bases__ 属性;如果为空则会添加所有类的终极基类 object。
    dict 字典包含类主体的属性和方法定义;它在成为 __dict__ 属性之前可能会被拷贝或包装。 

4. 下面两条语句会创建相同的 type 对象:
        class X:
            a = 1

        X = type('X', (), dict(a=1))

5. 提供给三参数形式的关键字参数会被传递给适当的元类机制 (通常为 __init_subclass__()),相当于类定义中关键字 (除了 metaclass) 的行为方式。

6. 在 3.6 版更改: type 的子类如果未重载 type.__new__,将不再能使用一个参数的形式来获取对象的类型

稍作解释

  1. 传入一个参数时,返回 object 的类型。 返回值是一个 type 对象,通常与 object.class 所返回的对象相同
a = 1
a.__class__
type(a)  # 跟上面的结果是一样的, 但不能1.__class__哦~
  1. isinstance
s1 = 'a'
isinstance(s1,str) # True
isinstance(s1,(str,int,list)) # 一样也是True
class A:
    pass
class B(A):
    pass

b = B()
isinstance(b,B)  # True
isinstance(b,A) # 也是True

# 但是type不一样,不会检查继承
type(b) == B # True
type(b) == A # False
  1. 上面是最简单的使用,最关键的来了,type传入三个参数时,返回一个新的 type 对象。 这在本质上是 class 语句的一种动态形式

    # 在Pycharm上查看type的定义
    class type(object):
        """
        type(object_or_name, bases, dict)  # 2种用法
        type(object) -> the object's type # 上面讲的用法1
        type(name, bases, dict) -> a new type # 创建动态类的另外一种方式
        """
    
    1. name 字符串即类名并会成为 __name__ 属性;# 字符串类型
    2. bases 元组包含基类并会成为 __bases__ 属性;如果为空则会添加所有类的终极基类 object。 
    3. dict 字典包含类主体的属性和方法定义;它在成为 __dict__ 属性之前可能会被拷贝或包装。# 这里可以填写类属性、类方式、静态方法,采用字典格式,key为属性名,value为属性值
    

动态创建类

  • 官网案例

    class X:
        a = 1
    # 等价于
    X = type('X', (), dict(a=1))
    
  • 我们做点拓展

  • 示例1:一些典型的错误

    type('A',,)  # SyntaxError: invalid syntax
    
    type('A',(),)  # TypeError: type() takes 1 or 3 arguments
    
    type('A',(),1) # TypeError: type.__new__() argument 3 must be dict, not int
    
    
    
  • 示例2:类的创建

    # 依旧是错误的示范
    type('A',(),{}) # 相当于创建了一个类的名字叫A
    a = A()  # NameError: name 'A' is not defined  但你不能这么用
    
    # 正确的
    B= type('B',(),{})
    b = B()
    type(b)  # __main__.B
    
    # 诡异的(不推荐)
    D = type('C',(),{})
    x = C()  # 哪个是对的?
    y = D()  # 哪个是对的?
    
    
    
    
    D = type('C',(),{})
    x = C()  # 提示报错了,NameError: name 'C' is not defined
    
    y = D() # 对的
    type(y)  # 结果是啥?  __main__.C 你想到了吗?当然也还是可以理解的
    # type('C',(),{}) 创建一个名叫C的类
    # 但D这个变量接收了上面的返回值,C是没有这个变量的(不在内存中)
    
  • 示例3:带继承的类的创建

    class A:
        pass
    
    B = type('B',('A'),{})  # 这个错比较明显 
    # TypeError: type.__new__() argument 2 must be tuple, not str
    
    
    
    class A:
        pass
    
    B = type('B',('A',),{})
    
    TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
    # 其实错在你不能用'A',这是个str,实际上此处应该是一个类名
    
    • 应该这样
    class A:
        pass
    
    B = type('B',(A,),{})
    b = B() # 实例化没问题
    print(B.__mro__) # mro也是ok的
    # (<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
    
    

  • 示例4:带类属性的创建

    C = type('C',(),{'name':'wuxianfeng'})
    print(C.name)  # 输出 wuxianfeng
    
    • 注意是类属性,并不是实例属性

  • 示例5:带实例方法的创建

    def say(self):
        self.name = 'wuxianfeng'
        print(f'calling method say, my name is {self.name}')
    Person = type('Person',(),{'say':say})
    wxf = Person()
    wxf.say()   # calling method say, my name is wuxianfeng
    
    
  • 多一个例子(因为这是最常见的做法)

    def run(self):
        return 'calling run'
    
    Foo = type('Foo',
               (object,),
               {'value':1,
                'func':lambda self:'calling func',
               'run':run})
    foo = Foo()
    print(foo.value) # 输出 1
    result = foo.func() # 接收返回值‘calling func’
    print(result)   # 输出 calling func
    print(foo.run()) # 输出 calling run
    

说在最后

  • class创建类的本质就是用type创建,所以可以说python中所有类都是type创建的,包括整数、字符串、函数以及用户自定义的类

  • 当type()只有一个参数时,其作用就是返回变量或对象的类型

  • 当type()有三个参数时,其作用就是创建类对象

  • 通过type添加的属性是类属性,不是实例属性

  • type就是Python的内建元类

与Python常见面试题011. 如何在Python中动态创建类?相似的内容:

Python常见面试题011. 如何在Python中动态创建类?

011. 如何在Python中动态创建类? 说在前面 答案是type 你印象中的type是用来查看对象的类型的 li = [] type(li) # 得到list 对自定义的类是这样的 class Person: pass wuxianfeng = Person() type(wuxianfeng)

Python常见面试题001-005,涉及深浅拷贝、MRO、函数可变参数、作用域、is和==的区别等

Python常见面试题001-005 参考资料 https://github.com/taizilongxu/interview_python https://github.com/hantmac/Python-Interview-Customs-Collection https://github.

Python常见面试题006 类方法、类实例方法、静态方法有何区别?

006. Python中类方法、类实例方法、静态方法有何区别? 全部放一个里面篇幅过大了,就拆分成1个个发布 示例代码 class Human: def __init__(self, name): self.name = name def say(self): print(f'我的名字是{self.

Python常见面试题007. 谈谈Python中__init__和__new__的区别

007. 谈谈Python中__init__和__new__的区别 python中关于dunder method双下方法,或magic method魔术方法的描述多在 https://docs.python.org/zh-cn/3.9/reference/datamodel.html#special

Python常见面试题008. 谈谈python中的解包

008. 谈谈python中的解包 这是个简单的知识点,但有的学员并不理解 unpacking解包 解,对应的是*或者**,也有自动解包之说 包对应的可迭代对象 自动解包 赋值的demo a,b = [1,2] print(a) # 1 print(b) # 2 将容器里面的元素逐个取出来分别赋值

Python常见面试题009. 元组和列表有什么区别

009. 元组和列表有什么区别 这个题是简单的,但要拿满分或者说高分不容易 相同点 | 共性 | 说明 | | | | | 可以存放任意元素 | 一般都放同类型 | | 支持索引访问 | 甚至是负数 | | 支持切片操作 | | | 逗号分隔元素 | | | 都是有序集合(容器) | | | 可以随

Python常见面试题010. Python的int占多大内存?

010. Python的int占多大内存? 示例代码 import sys a = 0 print(a.__sizeof__()) # 24 print(sys.getsizeof(a)) # 24 所以答案是24?并不是!看下面 import sys b = 1 print(b.__sizeof_

Python常见面试题012. 可迭代对象和迭代器有啥区别?

012. 可迭代对象和迭代器有啥区别? 2者不是一回事(废话) 比如 from collections.abc import Iterable,Iterator print(isinstance([1, 2], Iterable)) # True 列表是可迭代对象 print(isinstance(

Python常见面试题013.请说出下面的代码返回结果是什么?

013.请说出下面的代码返回结果是什么? *的坑;简单题 参考:https://docs.python.org/zh-cn/3.9/library/stdtypes.html#typesseq 示例代码 lists = [[]] * 3 lists[0].append(1) 请问lists此时是什么

Python常见面试题014.请说出下面的代码返回结果是什么?

示例代码 def fun(a, b, c, d): nums = [] for num in range(a, b): nums.append(lambda: num ** c) return nums[d]() print(fun(1, 5, 2, 0)) print(fun(1, 5, 2, 1