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()
两个重要的属性:
-
__class __: 表示这个对象是谁创建的; -
__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。
objectis the ultimate base class of all classes.
-
typeis the metaclass of all classes. -
typeis itself a subclass ofobject. -
objectis itself an instance oftype.
二、元类:metaclass
1、type
就像str是用来创建字符串对象的类,int是用来创建整数对象的类,而type就是创建类对象的类。
- 类也是对象,类型是type。class的类型是type。
typeis 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在背后用来创建所有类的元类
-
type 可以创建类
-
type 创建的对象拥有创建对象的能力(也就是类)
-
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 (Personobject). -
__call__in a metaclass → runs every time you doPerson(), 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
-
Python calls the class’s
__new__→ allocates the instance (memory). -
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:
-
Python calls the metaclass’s
__call__(not the class’s). -
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__.