【引子】

  虽然我们可以通过“class”语句来定义“类”,但是要想更加细粒度的控制“类”的创建,要使用元类编程才能实现。

  比如说我们要实现这样的一个约束、所有项目中用到的类都应该要为它定义的方法提供文档字符字符串。有两条可行

  的路径 1):依赖工程师的自觉、自律 让他们在定义每一个方法的时候都为方法增加文档字符串, 2):通过元类来做一些

  控制,如果工程师没有为方法提供文档字符,那么就直接报错,直到工程师为方法提供字符串为止。

【实现1】

  依赖工程师的自觉、自律为方法增加文档字符串

class Person(object):
name = None
def __init__(self,name):
self.name=name def say_hello(self):
"""
print hello My name is xxx ...
"""
print("hello My name is {self.name}".format(self=self))

  我们如何能保证每一个方法都增加了文档字符串呢?我一方面要依靠“自觉”另一方面要依靠“纪律性”,最好要有专门的人来做代码

  审核。

【实现2】

  通过元类来实现这个约束

  第一步:定义一个元类来审核class

class DocMeta(type):
"""
检查方法是否有提供文档字符串
"""
def __init__(self,name,base,attrs):for key,value in attrs.items():
if key.startswith('__'):
#跳过魔术方法
continue
if not hasattr(value,"__call__"):
#跳过字段
continue
#如果能进入到这里、那么一定是方法了、于是检查方法有没有文档字符串.
if not getattr(value,"__doc__"):
#没有文档字符串的情况下就报错
raise TypeError("{0} must have a docstring".format(key))
super().__init__(name,base,attrs)

  

  第二步:定义一个通用的基类、以后所有要实现这一约束的类都继承自它

class Documented(metaclass=DocMeta):
pass

  注意这个项的元类是我们刚才定义的“DocMeta”类

  第三步:让项目中的类继承自这个基类

class Person(Documented):
name = None
def __init__(self,name):
self.name=name def say_hello(self):
"""
print hello My name is xxx ...
"""
print("hello My name is {self.name}".format(self=self))

  

  第四步:和使用普通的类一样使用Person类

if __name__=="__main__":
p = Person("welson")
p.say_hello() #hello My name is welson

【总结】

  以上代码由于在say_hello 方法中提供了文档字符串、如果没有提供的话、在创建Person类的时候就会报错了

Traceback (most recent call last):
File "main.py", line 31, in <module>
class Person(Documented):
File "main.py", line 24, in __init__
raise TypeError("{0} must have a docstring".format(key))
TypeError: say_hello must have a docstring

  全部代码如下:

"""
Python元类编程的一个例子
""" __version__ = '0.1'
__author__ = '蒋乐哥哥' class DocMeta(type):
"""
检查方法是否有提供文档字符串
"""
def __init__(self,name,base,attrs):
for key,value in attrs.items():
if key.startswith('__'):
#路过魔术方法
continue
if not hasattr(value,"__call__"):
#跳过字段
continue
#如果能进入到这里、那么一定是方法了、于是检查方法有没有文档字符串.
if not getattr(value,"__doc__"):
#没有文档字符串的情况下就报错
raise TypeError("{0} must have a docstring".format(key))
super().__init__(name,base,attrs) class Documented(metaclass=DocMeta):
pass class Person(Documented):
name = None
def __init__(self,name):
self.name=name def say_hello(self):
"""
print hello My name is xxx ...
"""
print("hello My name is {self.name}".format(self=self)) # 在不为方法提供文档字符串的情况下会直接报错
#class Person(Documented):
# name = None
# def __init__(self,name):
# self.name=name
#
# def say_hello(self):
# print("hello My name is {self.name}".format(self=self)) if __name__=="__main__":
p = Person("welson")
p.say_hello()

----------------------------------------

最新文章

  1. HDU 5920 Ugly Problem 高精度减法大模拟 ---2016CCPC长春区域现场赛
  2. java出错
  3. HTML5自学笔记[ 18 ]canvas绘图基础5
  4. 为PHP开发者准备的12个调试工具
  5. sql索引碎片产生的原理 解决碎片的办法(sql碎片整理)(转)
  6. centos7 jsoup java.net.UnknownHostException
  7. AngularJs学习笔记2——四大特性之MVC
  8. 拦截API 注入进程
  9. a标签的伪元素的应用——link,hover,visited,active
  10. linux杀毒软件ClamAV的安装使用
  11. C++ Exception机制
  12. 更多more 123123循环
  13. python unittest setUp 和 setUpClass 区别
  14. MQTT协议
  15. 解读使用Daisy-chain(菊花链)方式筛选一定范围内素数的代码
  16. minicom的安装及配置
  17. 第一章javascript词法结构笔记摘要
  18. java的@PostConstruct注解
  19. Java与WCF交互(一):Java客户端调用WCF服务 【转】
  20. Android基础总结(六)Activity

热门文章

  1. 转:nginx模块开发——handler(一)
  2. MySQL源代码解读
  3. IE浏览器兼容方案
  4. 玩转Bootstrap
  5. kettle Spoon.bat运行闪退
  6. 8、redis之事务1-redis命令
  7. python之模块poplib之常见用法
  8. Lotus Domino和关系型数据库(LEI,DESC,JDBC连接)
  9. 【linux】linux 环境下 安装禅道(转载) -- 跟web服务器无关,无视apache、nginx!!!
  10. java 获取计算机名称, ip, mac地址