02 Python语法 (5) 面向对象 (面试!!)

一、一切皆对象

1、类本身也是对象

在面向对象的语言中,类就是一组用来描述如何生成一个对象的代码段。但是,Python中的类还远不止如此。类同样也是一种对象。只要你使用关键字class,Python解释器在执行的时候就会创建一个对象。下面的代码段:

class Person(object):
    pass

上面的代码,将在内存中创建一个对象,名字就是Person。这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。但是,它的本质仍然是一个对象。

所以,在Python中有两种对象:

  • 类型(类)对象:可以被实例化和继承; Person

  • 非类型(实例)对象:不可以被实例和继承。 P = Person()

在Python里,int整形是对象,整数2也是对象,定义的函数、类都是对象,定义的变量也是对象。总之,在Python里能用到的都可以称之为对象。

再来了解一下Python中对象之间的两种关系。面向对象体系中有两种关系:

  • 父子关系:通常描述为“继承关系的两个类,被继承的那个类是父类,继承的那个类是子类”。

    • Animal <-----------> Dog
  • 类型实例关系:这种关系存在于两个对象中,其中一个对象(实例)是另一个对象(类型)的具体实现。

    • Person <-----------> p = Person()

两个重要的属性:

  1. __class __ : 表示这个对象是谁创建的;

  2. __bases__ : 表示这个类的*父类是哪个类?

class Person(object):
    pass


if __name__ == '__main__':
    print(Person.__class__) 是由class `type` 创建的
    p = Person()
    print(p.__class__) __main__.Person
    print(p.__bases__) 报错
    print(Person.__bases__) 是object

    print(int.__class__) <class 'type'>
    print(int.__bases__) <class 'object'>

	a = 100
	a.__bases__ 报错,没有集成
	a.__class__ <class 'int'>

从上面的代码可以知道:

  • type为对象的顶点,所有对象都创建自type。

  • object为类继承的顶点,所有类都继承自object。

    • object is the ultimate base class of all classes.
  • type is the metaclass of all classes.

  • type is itself a subclass of object.

  • object is itself an instance of type.

二、元类:metaclass

1、type

就像str是用来创建字符串对象的类,int是用来创建整数对象的类,而type就是创建类对象的类。

  • 类也是对象,类型是type。class的类型是type。
  • type is the creator of all classes

类本身不过是一个名为type类的实例。在 Python的类型世界里,type这个类就是造物的上帝。用户自定义类,只不过是type类的call运算符的重载。当我们定义一个类的语句结束时,真正发生的情况,是 Python 调用 type 的__call__运算符。简单来说,当你定义一个类时,写成下面时:

class Person(object):
    name = 1

Python解释器真正执行的代码是:

class = type(classname, superclasses, attributedict)

这里等号右边的type(classname, superclasses, attributedict),就是type 的__call__运算符重载,它会进一步调用:

type.__new__(typeclass, classname, superclasses, attributedict)
type.__init__(class, classname, superclasses, attributedict)

代码验证:

class Person(object):
    name = 1


if __name__ == '__main__':
    c1 = Person
    print(c1.name)

    c2 = type('Person', (object,), {'name': 1})
    print(c2.name)

    p1 = c1()
    p2 = c2()

    print(p1.__class__)
    print(p2.__class__)

type就是Python在背后用来创建所有类的元类

  1. type 可以创建类

  2. type 创建的对象拥有创建对象的能力(也就是类)

  3. type 就是 Python 中所有类的元类(metaclass)

所有的class都是type的instance

2、元类的定义

我们知道了Python中的类也是对象。元类就是用来创建这些类(对象)的,元类就是创建类的类。元类 并不是某一个类的名字,它是一个概念,是一种Python的思想。它可以帮我们动态的创建类。

1) 使用函数的方式来定义一个元类

需求:动态改变类中所有的属性名都改成大写, 类的私有属性除外

# 这三个形参,就是用来描述一个类
def UpperMetaClass(class_name, class_parents, class_attrs):
    print(class_attrs)
    new_attrs = {}
    for name, value in class_attrs.items():
        if not name.startswith('__'):
            new_attrs[name.upper()] = value
        else:
            new_attrs[name] = value

    return type(class_name, class_parents, new_attrs)


class Person(object, metaclass=UpperMetaClass):
    name = 'zhangsan'
    __age = 33

2) 使用类的方式来定义一个元类

class UpperMetaClass(type):

    # 如果需要,重新创建Person类对象,需要重写__new__
    def __new__(cls, class_name, class_parents, class_attrs):
        new_attrs = dict(
            (name, value) if name.startswith('__') else (name.upper(), value) for name, value in class_attrs.items())
        return super().__new__(cls, class_name, class_parents, new_attrs)


class Person(object, metaclass=UpperMetaClass):
    name = 'zhangsan'
    __age = 33

三、单例模式

  • singleton pattern

一个类在整个程序运行过程中,始终只能有一个实例对象。

1、通过私有属性设计单例

class SingletonPerson(object):

    def __new__(cls, *args, **kwargs):
        # 采用当前类的私有属性存放,实例对象
        if not hasattr(cls, '_instance'):
            cls._instance = super(SingletonPerson, cls).__new__(cls, *args, **kwargs)
        return cls._instance


p1 = SingletonPerson()
p2 = SingletonPerson()

print(p1 is p2)
  • __new__ in a metaclass → runs once, when the class itself is created (Person object).

  • __call__ in a metaclass → runs every time you do Person(), i.e., when you create instances of that class.

2、通过元类动态的设计单例

# 只用元类的知识,定义一个单例模式
# 使用我们定义的元类创建出来的类,都是单例的

class SingletonMetaClass(type):

    def __init__(self, *args, **kwargs):  #  对类进行初始化
        print(" 开始执行元类中的__init__ 函数")
        self._abc = None
        super(SingletonMetaClass, self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        print(" 开始执行元类中的__call__ 函数")
        if self._abc is None:
            self._abc = super(SingletonMetaClass, self).__call__( *args, **kwargs)
        return self._abc


class Person(object, metaclass=SingletonMetaClass):
    pass


p1 = Person()  # p1 都是实例对象
p2 = Person()

print(p1 is p2)

1. What happens when you do a = Person()

Normal class

  1. Python calls the class’s __new__ → allocates the instance (memory).

  2. Then it calls the class’s __init__ → initializes the instance.

👉 So in a normal class, if you want to control instance creation (like singleton), you override __new__.


Class with a metaclass

When you write Person() and Person is built with a custom metaclass:

  1. Python calls the metaclass’s __call__ (not the class’s).

  2. Inside type.__call__ (the default), it does:

    • Call the class’s __new__ to make an instance.

    • Call the class’s __init__ to initialize it.

👉 So in a metaclass, the right hook to intercept instance creation of classes that use it is __call__.