python元编程 实际应用_Python 元编程

Python 元编程要解决的问题

python 元编程目的是为了让代码更加的灵活,实现的手段就是用代码生成代码,用代码修改代码。

sqlpy-0610-meta.jpg

元编程的基础

第一条、在 Python 的世界里一切都是对象,对象是类的实例,而类是元类的实例,元类也是类。

第二条、创建实例由类的 __new__ 方法完成,创建完成之后交由 __init__ 初始化。

为了一步步加大难度,在这里我们先会用 __new__ 修改实例的创建过程(实现单例模式),然后再使用 __new__ 为类动态的添加属性。

用元编程实现单例

由于“实例”是由类的 __new__ 方法创建的,如果 __new__ 的逻辑只有在第一次运行时才会创建实例,其它情况都是直接返回第一次运行时所创建的实例,这样的话不就完成单例模式了吗?

#!/usr/bin/evn python3

class Singleton(object):

# 单例模式的那个实例会交由这个变量名引用

_instance = None

def __new__(cls, *args):

"""

"""

if cls._instance is None:

# 只有在第一次运行里 cls._instance 才会是 None

print("Singleton.__new__ function create instance.")

cls._instance = super().__new__(cls)

return cls._instance

def main():

s0 = Singleton()

s1 = Singleton()

if id(s0) == id(s1) == id(Singleton._instance):

print("is the same")

if __name__ == "__main__":

main()

运行效果。

python3 main.py

Singleton.__new__ function create instance.

is the same

可以看到 ·Singleton.new function create instance.只打印了一次,说明只创建了一次实例,id(s0) == id(s1) == id(Singleton._instance)` 三个对象的 id 相同也证明了这一点。

第一阶段总结

1、可以看到元编程并不难,只要理解了“实例”是由“类”的 __new__ 方法创建的,这一个关键点就行了。至于是如何创建的这个就交给解释器自己解决吧,这也就是我们在创建实例是直接使用 super().__new__(cls) 的原因,套娃就行了。

2、上面的例子中我们的日志格式是 [类名].[方法名] [日志内容] 按这个格式打印日志是可以比较容易的找到有问题的函数,那后面我们自己写的类也都这样干吧。

用传统写法给类加上日志

假设我们有两个类一个是 Person,另一个是 Student 都要给他们的方法上加日志,在不用元类的时候可以这样实现。

#!/usr/bin/evn python3

import logging

# 配置日志格式

lgr = logging.getLogger("sqlpy")

lgr.setLevel(logging.DEBUG)

stream_handler = logging.StreamHandler()

formatter = logging.Formatter(

'%(name)s %(message)s')

stream_handler.setFormatter(formatter)

lgr.addHandler(stream_handler)

class Person(object):

# 日志对象

logger = lgr.getChild("Person")

# 名字

name = ""

def __init__(self, name="tom"):

self.name = name

def say_hello(self):

logger = self.logger.getChild("say_hello")

logger.info(f"My name is {self.name} .")

class Student(Person):

# 日志对象

logger = lgr.getChild("Student")

# 专业名

major = 0

def __init__(self, name="tom", major=16):

self.major = major

super().__init__(name)

def say_hello(self):

logger = self.logger.getChild("say_hello")

logger.info(f"My name is {self.name}. My major is {self.major} .")

def main():

p = Person()

p.say_hello()

s = Student()

s.say_hello()

if __name__ == "__main__":

main()

运行效果。

python3 main.py

sqlpy.Person.say_hello My name is tom .

sqlpy.Student.say_hello My name is tom. My major is 16 .

传统写法存在的问题

传统写法的一个问题在于,要用日志功能就要 "显示的在类上加一个属性",可不可以把这个去掉呢?我们没有必要一直重复下去。

想想“类”是元类的实例,我们只要在元类创建类时把 logger 加上去就行了,这样就一步到位了。

元类 type

type 类是所有类的元类,也就是说默认情况下所有的类都由 type 创建,可以通过下面的代码验证。

#!/usr/bin/evn python3

import logging

# 配置日志格式

lgr = logging.getLogger("sqlpy")

lgr.setLevel(logging.DEBUG)

stream_handler = logging.StreamHandler()

formatter = logging.Formatter(

'%(name)s %(message)s')

stream_handler.setFormatter(formatter)

lgr.addHandler(stream_handler)

class Person(object):

# 日志对象

logger = lgr.getChild("Person")

# 名字

name = ""

def __init__(self, name="tom"):

self.name = name

def say_hello(self):

logger = self.logger.getChild("say_hello")

logger.info(f"My name is {self.name} .")

if __name__ == "__main__":

p = Person()

print(f"实例 p 由 {type(p)} 创建")

print(f"实例 {Person} 由 {type(Person)} 创建")

运行效果。

python3 main.py

实例 p 由 创建

实例 由 创建

也就是说如果我们想在创建类的时候做一点其它的手脚,只要实现一个 type 的子类,并把我们的类标记成用这个子类创建,而不是用默认的 type 创建。

用元类实现为类添加日志对象

你应该已经想到了,就是重写一个 type.__new__ 方法。

#!/usr/bin/evn python3

import logging

# 配置日志格式

lgr = logging.getLogger("sqlpy")

lgr.setLevel(logging.DEBUG)

stream_handler = logging.StreamHandler()

formatter = logging.Formatter(

'%(name)s %(message)s')

stream_handler.setFormatter(formatter)

lgr.addHandler(stream_handler)

class LoggerMeta(type):

"""

"""

def __new__(cls, classname, bases, attrs):

"""

classname 要创建的类名

bases 类的基类

attrs 类的属性

"""

# lgr.info(

# f"cls = {cls} classname = {classname} bases = {bases} attrs = {attrs}")

# 添加日志对象

attrs.update({'logger': lgr.getChild(classname)})

return super().__new__(cls, classname, bases, attrs)

class Person(object, metaclass=LoggerMeta):

# 名字

name = ""

def __init__(self, name="tom"):

self.name = name

def say_hello(self):

logger = self.logger.getChild("say_hello")

logger.info(f"My name is {self.name} .")

if __name__ == "__main__":

p = Person()

p.say_hello()

运行效果。

python3 main.py

sqlpy.Person.say_hello My name is tom .

第二阶段总结

元类算是 Python 的黑魔法,是通往代码定义代码的大门。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值