优质文章,第一时间送达!
作者:🐲礁sir
这两天写一个爬虫的时候遇到一个场景,要求写一个类放在代码中运行,这个类针对不同的网站会有统一的验证有效性,清理数据步骤,但是不同网站每个步骤具体操作内容是不同的。
也就是说,要求写一个类,函数名称是统一的,但是要根据初始化实例的时候传入的域名修改每个步骤的操作内容。
于是我想到了材料里面的内容。
材料
functiontools.singledispatch
抽象基类与切面
多重继承与MRO
重写,重载与猴子补丁
运算符重载
元编程
料理过程
functiontools.singledispatch:
抽象基类:
切面(Mixins):
重写(override):
函数重载(overload)和运算符重载:
猴子补丁和鸭子类型:
成品1:
类文件(SampleOperator.py):
from sample_operations import *
class SampleOperator:
def __init__(self, domain):
# 根据传入的域名,确认返回什么样的方法
self.domain = domain.replace('.', '_')
self.special_methods = ['is_valid', 'clean_data']
self.dynamic_load
# 用反射的方式加载方法
def dynamic_load(self):
print('--------{domain} start load--------'.format(domain=self.domain))
for method in self.special_methods:
print(method, hasattr(self, method))
special_method = '_'.join([method, self.domain])
setattr(self, method, globals[special_method])
print(method, hasattr(self, method))
func = getattr(self, method)
func
print('--------{domain} end load--------'.format(domain=self.domain))
if __name__ == '__main__':
test = SampleOperator('test.com')
test.is_valid
test.clean_data
abc = WebsiteOperator('abc.com')
abc.is_valid
abc.clean_data
方法文件(sample_operations.py):
def is_valid_test_com:
print('is_valid_test_com')
def clean_data_test_com:
print('clean_data_test_com')
def is_valid_abc_com:
print('is_valid_abc_com')
def clean_data_abc_com:
print('clean_data_abc_com')
运行效果: 实现了之前设想的通过传入不同域名来动态替换对应的方法内容,用同一个方法名调用
python SampleOperator.py
--------test_com start load--------
is_valid False
is_valid True
is_valid_test_com
clean_data False
clean_data True
clean_data_test_com
--------test_com end load--------
is_valid_test_com
clean_data_test_com
--------abc_com start load--------
is_valid False
is_valid True
is_valid_abc_com
clean_data False
clean_data True
clean_data_abc_com
--------abc_com end load--------
is_valid_abc_com
clean_data_abc_com
成品2:
>>> class A:
... def name(self):
... print('A')
...
>>> class B:
... def name(self):
... print('B')
...
>>> globals
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': , '__spec__': None, '__annotations__': {}, '__builtins__': , 'A': , 'B': }
>>> globals['A']
>>> globals['A']<__main__.a object at>
>>> globals['A'].name
A
进一步料理:考虑到可能会有十几二十个网站,每个网站最好是单独放一个文件,这样更方便管理和调整。于是想到了python的反射里面动态载入模块
先看看修改后的结构
tree
.
├── SampleOperator.py
└── web_operations
├── __pycache__
│ ├── abc_com.cpython-37.pyc
│ └── test_com.cpython-37.pyc
├── abc_com.py
└── test_com.py
SampleOperator.py
import importlib
class SampleOperator:
def __init__(self, domain):
self.domain = domain.replace('.', '_')
self.special_methods = ['is_valid', 'clean_data']
self.dynamic_load
# 用反射的方式加载方法
def dynamic_load(self):
print('--------{domain} start load--------'.format(domain=self.domain))
special_module = importlib.import_module(
'.'.join(['web_operations', self.domain])
)
for method in self.special_methods:
print(method, hasattr(self, method))
# 动态匹配到网站专门的方法
special_method = '_'.join([method, self.domain])
setattr(self, method, getattr(special_module, method))
print(method, hasattr(self, method))
print('--------{domain} end load--------'.format(domain=self.domain))
if __name__ == '__main__':
test = SampleOperator('test.com')
test.is_valid
test.clean_data
abc = SampleOperator('abc.com')
abc.is_valid
abc.clean_data
abc_com.py
def is_valid:
print('is_valid_abc_com')
def clean_data:
print('clean_data_abc_com')
test_com.py
def is_valid:
print('is_valid_test_com')
def clean_data:
print('clean_data_test_com')
作者个人简介:
大学时用记事本学写html以为这就是编程,后来学java才知面向对象的妙。再到python深入体会到函数式编程,响应式编程的独特风味。方才领悟到这就是我想要的味道。
于是看cpython源码的时候体会到c语言的稳当,再把python里面学到的语法,数据结构,算法,内在特性迁移对比到golang,ruby,lua,js等其他语言,最后能以更加宽广的视角看待编程语言。
我知道编程是片大海,我只是条小鱼,哪怕尽情扑腾也没有多少浪花,但是我只想慢慢感受。
回复下方「关键词」,获取优质资源
回复关键词「 pybook03」,立即获取主页君与小伙伴一起翻译的《Think Python 2e》电子版
回复关键词「入门资料」,立即获取主页君整理的 10 本 Python 入门书的电子版
回复关键词「m」,立即获取Python精选优质文章合集
回复关键词「」,将数字替换成 0 及以上数字,有惊喜好礼哦~
题图:pexels,CC0 授权。
好文章,我在看❤️
本文链接:https://my.lmcjl.com/post/11757.html
4 评论