背景 随着写的爬虫变多,从单文件变为多文件,一些方法也都单独写成工具类。在优化数据库执行的时候,忽然想到python有没有连接池,可以像Java一样,控制下连接数。这样我没有人为close,一旦资源池紧张,也不会影响。让连接池自己去关掉一些空闲连接给新的连接请求 。
在写的时候考虑到我mysql和sqlite3都在使用,可否都写到配置文件中?其实都写在py文件也没问题,只不过这样写的多了,相当于定义了很多变量,且看上去不是那么高级,在写完ini以后想到,我的配置文件可否也像java中properties一样来读取?
ini有很多现成的lib可用,properties没有,需要自己实现,看过几篇文章的思路后,自己来实现。
问题 配置文件如下
1 2 3 4 5 6 7 8 9 10 database.mysql.host_mysql=ip database.mysql.port_mysql=3306 database.mysql.database_mysql=dbname database.mysql.user_mysql=username database.mysql.password_mysql=password database.mysql.charset_mysql=utf8 database_sqlite3=amc.db
思路很简单,读取文件,然后首字母”#”开头的是注释忽略,非”#”开头且是x=y这种格式,带有”=”的认为是配置行。一般到这里逐行读取放到一个字典就行了,但是java中往往properties配置是支持 a.b=c这种格式的。
于是识别为配置行之后,再去按照”=”切割,右侧是值,左侧是key,当key中含有”.”的话,就按照”.”切割。
大致流程如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 初始 a.b.c=1 , key=a.b.c value=1 , 一个空字典{} | | | {'a' : {}} | | | {'a' : {'b' : {}}} | | | {'a' : {'b' : {'c' : 1 }}}
而写的get方法原理有点类似上面,支持 a.b.c去获取value
于是写下如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 Author: ryan Time: 2019 /08/13 0 :01 """ 像java一样读取 *.properties配置 传递值支持a.b.c """ class PropertiesRead :def __init__ (self, path ): self .path = path self .property_dict = self .get_property() def property_dict_treat (self, key_name, value, property_dict ): """ 递归 :param key_name: :param value: :param property_dict: :return: """ if key_name.find(& key = key_name.split(& key_name_temp = key_name[len (key) + 1 :] property_dict.setdefault(key, {}) self .property_dict_treat(key_name_temp, value, property_dict[key]) else : property_dict[key_name] = value def get_property (self ): """ 组成字典 :return: """ property_dict = {} with open (self .path, & for line in property_buffer.readlines(): line = line.strip().replace(& if line and line.find(& sp = line.split(& key = sp[0 ].strip() value = sp[1 ].strip() self .property_dict_treat(key, value, property_dict) return property_dict def get (self, key, temp_property=None ): """ Give key name then return value. :param key: :param temp_property: 用来存储取多层字典值时中间过程的value :return: """ if not temp_property: temp_property = self .property_dict if key.find(& temp_key = key[len (key.split(& temp_property = temp_property[key.split(& self .get(temp_key, temp_property) return temp_property.get(key) if name == "main" : filepath = "aaa.properties" pr = PropertiesRead(filepath) print (pr.get('database.mysql.host_mysql' ))
打印出来是None,就很奇怪,首先看temp_property得到的dict是什么,于是60后加一条print(temp_property)
1 2 3 4 5 6 7 8 9 10 {'mysql' : {'host_mysql' : 'ip' , 'port_mysql' : '3306' , 'database_mysql' : 'dbname' , 'user_mysql' : 'username' , 'password_mysql' : 'password' , 'charset_mysql' : 'utf8' }}{'host_mysql' : 'ip' , 'port_mysql' : '3306' , 'database_mysql' : 'dbname' , 'user_mysql' : 'username' , 'password_mysql' : 'password' , 'charset_mysql' : 'utf8' } 完整字典是 {'database' : {'mysql' : {'host_mysql' : 'ip' , 'port_mysql' : '3306' , 'database_mysql' : 'dbname' , 'user_mysql' : 'username' , 'password_mysql' : 'password' , 'charset_mysql' :'utf8' }}, 'database_sqlite3' : 'amc.db' }
可以看到,database
对应第一条,取到了,database.mysql
对应第二条,也正确的取到了。
然后就是从第二条字典,{'host_mysql': 'ip', 'port_mysql': '3306', 'database_mysql': 'dbname','user_mysql': 'username', 'password_mysql': 'password', 'charset_mysql': 'utf8'}
获取host_mysql
,显而易见,value
应当是: ip,然后再次调用自身,’.’ 为0,所以走最终的return。
设想是如此,打印确为None
。尝试了一些方法,都没有效果,网上搜了一下,直到看到这篇文章https://www.cnblogs.com/kuzaman/p/7563141.html
难道说调用自身之后,没有走到最终的return ?或者说,在self.get()后,并没有终止 ?
遂在self.get()之后加一行打印,说明了两个问题:
if中的self. get()并不一定代表本次if结束
只打印了两次...........
,说明第三次调用self后递归就已经结束了。
也就是说,每次调用自身,在递归没结束前,执行return temp_property.get(key), 是把结果传给了上一次调用的地方, 即上一次递归。 上面的代码self.get(),只是调用,没有做赋值操作,也没有return。
那么理论上最后一次return temp_property.get(key)
的值应该就是我们要的值,实验下,修改代码如下
可以看到第一条,打印的是ip,就是我们要的最终结果!!!
这边不太好描述为什么ip第一个被打印,画个图理解一下
【正常情况下,应该是下面这样子】
原始代码:
递归结束后,视线回到if内部,代码是接着往下走的,但是往下走已经没有代码了。(鼠标光标所在处)而且因为没有return,因此,代码继续执行if外部的部分,就最终return了。
而彼时的tempproperty和key,通过打印和图解,我们也看到了,这就是最初版本的key和最初版本的temp property(也就是sekf.property_dict)啊,a.b.c这样的格式的key肯定是没有值的,于是返回了None。
上门文章说的是循环中,不断调用func自身,我这边虽然是if判断,但大体相似,可能是一样的问题,于是在调用自身的代码前加上return ,需要返回每次的递归结果,变成这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def get (self, key, temp_property=None ): """ Give key name then return value. :param key: :param temp_property: 用来存储取多层字典值时中间过程的value :return: """ if not temp_property: temp_property = self .property_dict if key.find('.' ) > 0 : temp_key = key[len (key.split('.' )[0 ])+1 :] temp_property = temp_property[key.split('.' )[0 ]] return self .get(temp_key, temp_property) return temp_property.get(key)
这样就可以达到流程图的效果,第三次递归,取到最终结果返回给了初次递归的流程,继续往下走,return,结束整个方法。
总结 上面的过程不太明显,如果换成for循环更好理解递归这个概念,而递归是经常被用到的一环,在解决问题的过程中,有些地方还有些粗糙,有时间需要重新学习和梳理一下算法基础。另外,不理解的时候,画画图是件不错的选择。