1. 元类
1.1 理解类、对象
Python中一切皆对象。 --> 所以类也是对象 --> 那么,谁创建了类? --> 元类
元类:类的类,也就是类的模板。元类的实例为类,正如类的实例为对象。
- 分支语句动态创建类:结合分支语句等,使用class关键字动态的创建类。当使用class关键字时,Python解释器自动创建这个对象。
- type函数动态创建类:
- type函数功能一:判断对象的类型。–> eg:
type('abc')
- type函数功能二:动态的创建类。type可以接受一个类的描述作为参数,然后返回一个类。
- type函数功能一:判断对象的类型。–> eg:
# type函数创建类:
# 语法:type(函数名, (父类, ), {属性, 函数, ...})
def hello():
print("--hello--")
myclass = type("MyClass", (object, ), {'my_args': 'haha', 'hello': hello})
print(myclass.my_args)
myclass.hello()
1.2 元类的使用场景
元类的使用:
- 拦截类的创建
- 修改类
- 返回修改之后的类
动态生成类,不能控制类是如何生成的。python3 的metaclass可动态创建类。
4). 很多Web框架都会使用metaclass 来创建类。掌握元类对理解源代码至关重要。eg: ORM框架类
应用:自定义元类实现单例模式
class Singleton(type):
"""自定义元类实现单例模式"""
cache = {}
def __call__(cls):
if cls not in cls.cache:
cls.cache[cls] = super(Singleton, cls).__call__()
return cls.cache[cls]
class Person(object, metaclass=Singleton): # metaclass一个指定元类的参数
pass
if __name__ == '__main__':
# 注意:Person是Singleton元类实例化出的对象, Person()就是对象(), 执行Singleton.__call__魔术方法.
p1 = Person()
p2 = Person()
print("单例模式是否成功:", p1 is p2)
2. 抽象基类
抽象基类有两个特点:
- 规定继承类 (子类) 必须具有抽象基类中指定的方法,即用@abc.abstractmethod装饰的方法
- 抽象基类无法实例化
基于上述两个特点,抽象基类主要用于接口设计
实现抽象基类可以使用内置的abc模块
代码示例:
import abc
class Human(metaclass=abc.ABCMeta):
# 该装饰器规定:继承抽象类的子类在定义时必须要定义的抽象方法
@abc.abstractmethod
def introduce(self):
print("__抽象类__")
# 因为没有@abc.abstractmethod装饰器,在子类中hello方法不是必须的
def hello(self):
print("__hello__")
class Person(Human):
# 1. 规定子类中必须具有抽象基类指定的方法
def introduce(self):
print("__子类__抽象类__")
# 2. 抽象基类无法实例化
# TypeError: Can't instantiate abstract class Human with abstract methods introduce
# h = Human()
p = Person()
p.introduce()
3. 自省机制
自省机制:
- 在日常生活中,自省 (introspection) 是一种自我检查行为。
- 在计算机编程中,自省是指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。自省向程序员提供了极大的灵活性和控制力。
- 例如Python、Ruby、object-C、C++都有自省的能力,这里面的C++的自省的能力最弱,只能够知道是什么类型,而像Python可以知道是什么类型,还有什么属性。
Python中常见的自省机制:
函数用法有: dir()、type()、hasattr(),、setattr()、getattr()、delattr()、isinstance(),通过这些函数,我们能够在程序运行时得知对象的类型,判断对象是否存在某个属性,访问对象的属性。
>>> s = "abc"
# 1. 查看 s 的全面属性和方法
>>> dir(s)
['__add__',
'__class__',
'__contains__',
'__delattr__',
...
'translate',
'upper',
'zfill']
# 2. 返回对象类型
>>> type(s)
str
# 3. 判断对象类型,返回bool值
>>> isinstance(s, str)
True
>>> def hello():
...: pass
# 4. 设置对象属性,如 hello.name = 'xiaohong'
>>> setattr(hello, "name", "xiaohong")
# 5. 判断对象中是否包含某个属性,返回bool值
>>> hasattr(hello, "name")
True
# 6. 获取对象中的某个属性值
>>> getattr(hello, "name")
'xiaohong'
# 7. 删除对象中的某个属性
>>> delattr(hello, "name")
>>> hasattr(hello, "name")
False
4. __slots__
Python是动态语言,动态语言与静态语言的不同之处是:
- 动态语言:可以在运行的过程中,修改代码
- 静态语言:编译时已经确定好代码,运行过程中不能修改
如何要限制实例中的属性
- Python允许在定义class的时候,定义一个特殊的 __slots__变量,来限制该class实例能添加的属性。
- 使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
代码示例:
import time
class Date(object):
# 限制示例中能够添加的属性有:'__year', '__month', '__day'
__slots__ = '__year', '__month', '__day'
def __init__(self, year, month, day):
self.__year = year
self.__month = month
self.__day = day
@property
def year(self):
return self.__year
@property
def month(self):
return self.__month
@property
def day(self):
return self.__day
@classmethod
def today(cls):
time_tuple = time.localtime()
return cls(time_tuple.tm_year, time_tuple.tm_mon, time_tuple.tm_mday) # 返回实例化对象
def __str__(self):
return "%s-%s-%s" % (self.year, self.month, self.day)
if __name__ == '__main__':
date = Date(2020, 1, 4)
print("date数据类型: ", type(date))
print("date有year属性吗?", hasattr(date, "year"))
print("date有time属性吗?", hasattr(date, "time"))
# setattr(date, "time", "2020-11-11") # AttributeError: 'Date' object has no attribute 'time'
# print(getattr(date, "time"))
today = date.today()
print("today数据类型: ", type(today))
# print("今天是%s年%s月的第%s天" % (today.year, today.month, today.day))
print(today)
执行结果: