python如何设计工具类_拒绝重复造轮子!python实用工具类及函数大推荐!

从2015年转行程序员至今也有两年多了,当初自学的java却误打误撞进了python的坑,入职之后一天java也没有写过,或许这可以理解成缘分吧,哈哈!使用python工作久了,随手写一些小工具再所难免,不知不觉,我的工具包也增长到了几千行代码。其中有的函数依照别人的代码改写,也有的源于自己的灵感,我自认为还是挺好用的。现在统一规整后做成pip包,拿出来分享给大家使用。

函数都很小巧,代码实现相对简单,风格比较自由,尽可能不依赖其它安装包,极个别的工具依赖一些特定的驱动程序,比如redis,kafka, psutil,如果自己的项目没有相关需求,那么可以将需要的方法,类复制放到自己的项目中,不必完全安装ShichaoMa/toolkit。放心,我是不会追究版权的。

正文开始

安装toolkity

#python3测试有效

pip install toolkity

有朋友可能会想为什么不叫toolkit,我,也是那么想的。可是名字被别人占用了肿么办,只能在后面加个y,看起来正式而不失一点小俏皮。

安装完毕,来跟我学习几个常用函数的使用方法吧。Timer简单好用的计时器

class Timer(object):

"""

计时器,对于需要计时的代码进行with操作:

with Timer() as timer:

...

...

print(timer.cost)

...

"""

def __init__(self, start=None):

self.start = start if start is not None else time.time()

def __enter__(self):

return self

def __exit__(self, exc_type, exc_val, exc_tb):

self.stop = time.time()

self.cost = self.stop - self.start

return exc_type is None

有时我们想对我们写的代码执行时间进行统计,通常我们会使用如下代码:

import time

start = time.time()

...# 我们的代码

end = time.time()

cost = end - start

现在有了timer,一切都变的简单啦

# 使用方法:

In [2]: from toolkit.managers import Timer

In [3]: with Timer() as timer:

...: num = 10**20

...:

In [4]: print(timer.cost)

3.6716461181640625e-05

In [5]: print(timer.start)

1512270309.2823365

In [6]: print(timer.stop)

1512270309.2823732

同时,你还可以指定开始时间

In [9]: import time

In [10]: with Timer(start=time.time()-10) as timer:

...: num = 12**12

...:

In [11]: timer.cost

Out[11]: 10.000059366226196ExceptContext异常捕获上下文

class ExceptContext(object):

"""

异常捕获上下文

eg:

def test():

with ExceptContext(Exception, errback=lambda name, *args:print(name)):

raise Exception("test. ..")

"""

def __init__(self, exception=Exception, func_name=None,

errback=lambda func_name, *args: traceback.print_exception(*args) is None,

finalback=lambda got_err: got_err):

"""

:param exception: 指定要监控的异常

:param func_name: 可以选择提供当前所在函数的名称,回调函数会提交到函数,用于跟踪

:param errback: 提供一个回调函数,如果发生了指定异常,就调用该函数,该函数的返回值为True时不会继续抛出异常

:param finalback: finally要做的操作

"""

self.errback = errback

self.finalback = finalback

self.exception = exception

self.got_err = False

self.func_name = func_name or _find_caller_name(is_func=True)

def __enter__(self):

return self

def __exit__(self, exc_type, exc_val, exc_tb):

return_code = False

if isinstance(exc_val, self.exception):

self.got_err = True

return_code = self.errback(self.func_name, exc_type, exc_val, exc_tb)

self.finalback(self.got_err)

return return_code

通常我们想捕获异常时,会写如下代码

got_err = False

try:

1/0

except ZeroDivisionError:

import traceback

traceback.print_exc()

got_err = True

finally:

print(got_err)

现在我们可以用ExceptContext简单实现

with ExceptContext(ZeroDivisionError, finalback=lambda got_err: print(got_err)):

1/0

其中ExceptContext接收4个参数:param exception: 指定要监控的异常, 默认为Exception

:param func_name: 可以选择提供当前所在函数的名称,回调函数会提交到函数,用于跟踪,默认为None,自己判断调用函数名称

:param errback: 提供一个回调函数,如果发生了指定异常,就调用该函数,该函数的返回值为True时不会继续抛出异常 默认打出异常信息,返回True

:param finalback: finally要做的操作 默认返回是否发生异常。

通过自定义errback,我们可以对异常做任何想要的操作。debuggerdebug小工具

def debugger():

try:

debug = eval(os.environ.get("DEBUG"))

except Exception:

debug = False

if debug:

d = pdb.Pdb()

d.set_trace( sys._getframe().f_back)

有时我们可能会使用pdb来调试,经常会发生的情况是,我们在测试环境下调试,部署到生产之后,发现pdb.set_trace()忘记删除,导致代码在生产系统中卡住,这就很尴尬了。使用debugger,同时在测试环境中加入export DEBUG=True,可以轻松避免上述情况

import os

os.environ["DEBUG"] = True

from toolkit import debugger

def fun():

debugger()

....

fun()duplicate保序去重函数

def duplicate(iterable, keep=lambda x: x, key=lambda x: x, reverse=False):

"""

保序去重

:param iterable:

:param keep: 去重的同时要对element做的操作

:param key: 使用哪一部分去重

:param reverse: 是否反向去重

:return:

"""

result = list()

duplicator = list()

if reverse:

iterable = reversed(iterable)

for i in iterable:

keep_field = keep(i)

key_words = key(i)

if key_words not in duplicator:

result.append(keep_field)

duplicator.append(key_words)

return list(reversed(result)) if reverse else result

我们经常会遇到去重问题,比如

In [2]: ls = [3,4,5,2,4,1]

In [3]: ls = list(set(ls))

In [4]: ls

Out[4]: [1, 2, 3, 4, 5]

上述列表中有2个4,我们想去掉多余的4,但是不想顺序乱掉,如果使用list(set(ls))的方式,顺序会乱掉,因此我们可以使用duplicate函数来做

In [5]: from toolkit import duplicate

In [6]: ls = [3, 4, 5, 2, 4, 1]

# 正序去重

In [7]: duplicate(ls)

Out[7]: [3, 4, 5, 2, 1]

# 逆序去重

In [8]: duplicate(ls, reverse=True)

Out[8]: [3, 5, 2, 4, 1]

# 指定规则去重

In [10]: ls = [{"a": 3, "b": 4}, {"a":3, "b": 5}]

In [11]: duplicate(ls, key=lambda x: x["a"])

Out[11]: [{'a': 3, 'b': 4}]

# 去重后仅保留部分数据

In [12]: duplicate(ls, key=lambda x: x["a"], keep=lambda x: x["b"])

Out[12]: [4]chain_all连接多个可迭代对象

def chain_all(iter):

"""

连接多个序列或字典

:param iter:

:return:

"""

iter = list(iter)

if not iter:

return []

if isinstance(iter[0], dict):

result = {}

for i in iter:

result.update(i)

else:

result = reduce(lambda x, y: list(x)+list(y), iter)

return result

以前我们都使用Itertools.chain.from_iterable,但是这个函数无法连接字典同时返回的是可迭代器对象。

In [14]: chain_all([[1, 2], [1, 2]])

Out[14]: [1, 2, 1, 2]

In [15]: chain_all([{"a": 1}, {"b": 2}])

Out[15]: {'a': 1, 'b': 2}safely_json_loads安全的将字符串变成json对象

# 代码实现

def safely_json_loads(json_str, defaulttype=dict, escape=True):

"""

返回安全的json类型

:param json_str: 要被loads的字符串

:param defaulttype: 若load失败希望得到的对象类型

:param escape: 是否将单引号变成双引号

:return:

"""

if not json_str:

return defaulttype()

elif escape:

data = replace_quote(json_str)

return json.loads(data)

else:

return json.loads(json_str)

对于空字符串,返回默认类型,对于使用单引号包裹的字符串,将其转换成双引号

In [17]: json_str = """{'a': 1, "b": "i'm tom."}"""

In [18]: safely_json_loads(json_str)

Out[18]: {'a': 1, 'b': "i'm tom."}

# 上面的i'm tom中的单引号不会被转成双引号format_html_string 格式化html

def format_html_string(html):

"""

格式化html, 去掉多余的字符,类,script等。

:param html:

:return:

"""

trims = [(r'\n',''),

(r'\t', ''),

(r'\r', ''),

(r' ', ''),

(r'\u2018', "'"),

(r'\u2019', "'"),

(r'\ufeff', ''),

(r'\u2022', ":"),

(r"<([a-z][a-z0-9]*)\ [^>]*>", '<\g<1>>'),

(r'<\s*script[^>]*>[^<]*<\s*/\s*script\s*>', ''),

(r"?a.*?>", '')]

return reduce(lambda string, replacement: re.sub(replacement[0], replacement[1], string), trims, html)

去掉多余的html属性

In [20]: format_html_string("

")

Out[20]: '

'

....

除以上工具以外,还有很多工具非常有用,由于例子相对复杂,就不一一举例了,可以多关注我的github, 如ShichaoMa/proxy_factory项目,就有很多用例出现。

下面简单介绍一些其它工具重试器,包装函数对指定异常进行重试

def retry_wrapper(retry_times, exception=Exception, error_handler=None, interval=0.1):

"""

函数重试装饰器

:param retry_times: 重试次数

:param exception: 需要重试的异常

:param error_handler: 出错时的回调函数

:param interval: 重试间隔时间

:return:

"""

def out_wrapper(func):

@wraps(func)

def wrapper(*args, **kwargs):

count = 0

while True:

try:

return func(*args, **kwargs)

except exception as e:

count += 1

if error_handler:

result = error_handler(func.__name__, count, e, *args, **kwargs)

if result:

count -= 1

if count >= retry_times:

raise

time.sleep(interval)

return wrapper

return out_wrapper超时器,装饰函数并指定其超时时间

def timeout(timeout_time, default):

"""

Decorate a method so it is required to execute in a given time period,

or return a default value.

:param timeout_time:

:param default:

:return:

"""

class DecoratorTimeout(Exception):

pass

def timeout_function(f):

def f2(*args):

def timeout_handler(signum, frame):

raise DecoratorTimeout()

old_handler = signal.signal(signal.SIGALRM, timeout_handler)

# triger alarm in timeout_time seconds

signal.alarm(timeout_time)

try:

retval = f(*args)

except DecoratorTimeout:

return default

finally:

signal.signal(signal.SIGALRM, old_handler)

signal.alarm(0)

return retval

return f2

return timeout_function自实现groupby

def groupby(it, key):

"""

自实现groupby,itertool的groupby不能合并不连续但是相同的组, 且返回值是iter

:return: 字典对象

"""

groups = dict()

for item in it:

groups.setdefault(key(item), []).append(item)

return groupscookie解析

def parse_cookie(string):

"""

解析cookie

:param string:

:return:

"""

results = re.findall('([^=]+)=([^\;]+);?\s?', string)

my_dict = {}

for item in results:

my_dict[item[0]] = item[1]

return my_dict查找字符串函数和类

def load_function(function_str):

"""

返回字符串表示的函数对象

:param function_str: module1.module2.function

:return: function

"""

mod_str, _sep, function_str = function_str.rpartition('.')

return getattr(load_module(mod_str), function_str)

load_class = load_function查找字符串模块

def load_module(module_str):

"""

返回字符串表示的模块

:param module_str: os.path

:return: os.path

"""

return __import__(module_str, fromlist=module_str.split(".")[-1])获取可用端口

def free_port():

"""

Determines a free port using sockets.

"""

free_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

free_socket.bind(('0.0.0.0', 0))

free_socket.listen(5)

port = free_socket.getsockname()[1]

free_socket.close()

return port线程安全装饰器

def thread_safe(lock):

"""

对指定函数进行线程安全包装,需要提供锁

:param lock: 锁

:return:

"""

def decorate(func):

@wraps(func)

def wrapper(*args, **kwargs):

with lock:

return func(*args, **kwargs)

return wrapper

return decorate慢保存或提交

def call_later(callback, call_args=tuple(), immediately=True, interval=1):

"""

应用场景:

被装饰的方法需要大量调用,随后需要调用保存方法,但是因为被装饰的方法访问量很高,而保存方法开销很大

所以设计在装饰方法持续调用一定间隔后,再调用保存方法。规定间隔内,无论调用多少次被装饰方法,保存方法只会

调用一次,除非immediately=True

:param callback: 随后需要调用的方法名

:param call_args: 随后需要调用的方法所需要的参数

:param immediately: 是否立即调用

:param interval: 调用间隔

:return:

"""

def decorate(func):

@wraps(func)

def wrapper(*args, **kwargs):

self = args[0]

try:

return func(*args, **kwargs)

finally:

if immediately:

getattr(self, callback)(*call_args)

else:

now = time.time()

if now - self.__dict__.get("last_call_time", 0) > interval:

getattr(self, callback)(*call_args)

self.__dict__["last_call_time"] = now

return wrapper

return decorate类中的线程安全装饰器

def thread_safe_for_method_in_class(func):

"""

对类中的方法进行线程安全包装

:param func:

:return:

"""

@wraps(func)

def wrapper(*args, **kwargs):

self = args[0]

try:

self.lock.acquire()

return func(*args, **kwargs)

finally:

self.lock.release()

return wrapper获取ip

def get_ip():

"""

获取局域网ip

:return:

"""

netcard_info = []

info = psutil.net_if_addrs()

for k,v in info.items():

for item in v:

if item[0] == 2 and not item[1]=='127.0.0.1':

netcard_info.append((k,item[1]))

if netcard_info:

return netcard_info[0][1]

下面提供一些基础设施common_stop_start_control :提供开,关,重启,状态等命令行服务

Singleton:单例元类

Logger:提供日志服务

SettingsWrapper: 提供配置信息服务

ParallelMonitor: 使用Singleton构建, 多线程多进程统一管理器

LoggingMonitor:内建Logger和Settings

Service: 继承自ParallelMonitor,LoggingMonitor并实现common_stop_start_control 接口。编写微服务专用基类。

ProxyPool:基于redis的代理池,继承自Logger

ItemConsumer:kafka消费者,继承自Service

ItemProducer:kafka生产者,继承自Service

RedisQueue:基于redis的队列

FifoDiskQueue:持久化 FIFO 队列

Translate:翻译器,继承自ProxyPool,安装即可用的翻译器见ShichaoMa/translate_html

除以上工具之外,还有一些小工具,小函数,如果有兴趣的话,自己去源码里发现吧。

喜欢的可以给我点赞哦!

不懂的可以给我留言哦!

欢迎光临我的github 里面有十几个开源项目,总有一款适合你!

欢迎光临我的小博客,博客源码在github上有托管,一键安装,一键使用,博客中汇集了工作中常用问题的解决方案,希望能够为您提供帮助哦!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值