一般来说,使用 Python 写代码,大部分时候逃不开 for、else、if、elif、while、and、or、continue、break 这些关键字,以及加减乘除、比大小。
但如果稍微用用 @property
、@cached_property
(这个修饰器的实现前面章节有具体的代码)、hasattr
、getattr
;处理函数传入参数时,如有必要使用动态参数 *args
、**kwargs
;场景合适,把一个函数也当做一个参数传入到另外函数。那么,这些虽然也是基本的语法,但对于初学者来说,已经是高阶的应用了。
当我们将函数作为参数传入给另外的函数时,就已经是 函数式编程了。然而,引入任何一个新的,并且是大的技术性关键词,还是蛮麻烦的事情。就如面向对象编程
一样,我们可以压根不谈这个名词,就已经在实际中使用了。况且,仅把函数当参数对待,还远不算 函数式编程
,它应该是更学院派的,当然,这仅是我的个人意见。
Python 内置的模块 functools
有个 partial
的函数,我们可以固定某些参数的值,然后使用 partial
生成新的函数。这在实际场景中,是很常用的方法!
>>> from functools import partial
>>> def hello(w1='default word 1', w2='default word 2'):
... print(w1)
... print(w2)
...
>>> new_hello = partial(hello, w2='fixed w2')
>>>
>>> new_hello(w1='new w1')
new w1
fixed w2
我们介绍了 Magic Methods、Monkey Patch,它们也不算复杂,却能让我们在写代码的过程中,『为所欲为』的能力大幅提高。但是 Python 为了避免写代码的人过于『为非作歹』,有些地方还是额外设定了限制,比如一些原生的数据类型上就会有这样的限制。
我们希望一个字符串
也可以设定属性,那么一个变量在程序中流转的时候,就可以根据这个属性来做额外的对应了,同时也不会改变数据原来的类型 (还是字符串)。于是,我们尝试这样的代码,只是报错了:
>>> s = u'hello'
>>> s.a = 'my word'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'unicode' object has no attribute 'a'
但只要从 unicode 继承过来,构建一个新的 Class,并实例化之后,就又没有这个问题了:
>>> class UnicodeWithAttr(unicode):
... pass
...
>>> s = UnicodeWithAttr('hello')
>>> s.a = 'my word'
>>> print(s.a)
my word
简而言之,如果你在写代码的过程中,产生了一个想法,而且这个想法是自然的、能提高效率的,就尝试去实现它,有些时候可能会遇到限制,那么打破它就好了。
如果自己不能主动产生这个想法,那么在写代码的过程中,也偶尔去看看别人的代码(比如说引入到当前项目中其他人的 module),也会逐渐增加这方面的见识。
虽然已经特别介绍过 编码 了,但对于新手而言,编码的问题,是必然会遇到的。
除了普通的文本编码之外,还有特定格式的编码,JSON 之类的格式反倒还好一些,出错了就是出错了,很容易发现,而一些人们一直在使用的场景 (比如说 URL),很少注意到其编码的特殊性,反倒容易出现错误。
但且不用特别管它吧,肯定会遇到,也肯定会解决的。
对了,我们其实也可以参考一些既有成熟项目的 utils (可以理解为 通用工具
的意思),比如 Django,参考它的源码 https://github.com/django/django。Django 的 utils.encoding 中有个 smart_str
的函数,还是蛮有用的,在 Django 早先的版本 (Python 2 还大规模流行的时候) 中是叫 smart_unicode
的名字。
面对用户,我们谈用户体验。而在技术层面,通常是没有多少用户体验可言的。
写代码时,注定我们成不了懒人,因为要不断思考。而对 『懒』的理解,仁智各见,写代码本身要实现很多思维逻辑上的复用,而 复用 本身就是最大的 偷懒 了。
我个人认为,在实际代码过程中,有什么偷懒的办法,就想尽办法去偷懒,只要别舍了写代码的初心就好了,此时的偷懒,其实都是提高效率的一种方式。万物难两全,偷懒也会有副作用,对这些副作用做一个权衡就好,毕竟,有些场景的限制,副作用也没有多大发挥的空间,也就算不上什么副作用了。
def get_value_from_data(data, attr, default=None):
if isinstance(data, dict) and attr in data:
return data[attr]
try:
attrs = attr.split('.')[:25] # 最多允许25层遍历
for attr in attrs:
attr = attr.strip()
if isinstance(data, dict):
data = data.get(attr, None)
else:
try:
data = getattr(data, attr, None)
except:
return data
if data is None: # 到头了
if default is not None:
return default
else:
return None
except RuntimeError: # 一般是在外部调用 g/request (Flask)
return None
return data
在一个项目中,我把数据的调用
放在外部的 Web 页面模板中,一个数据
可能是一个字典类型,也可能是其它的 Python 对象,dict 要获得其 key 对应的值,可以用 the_dict.get(key, default_value)
的方式,而其它数据对象一般是 the_object.key
或者 getattr(the_object, 'key', default_value)
,但在 API 的调用中,统一使用了 obj.key
的方式,这样的体验会好很多。
另外的一种情况,是 obj.sub_obj.sub_sub_obj.key
这种连续的调用,有时候比如 sub_obj
已经是 None 了,后面的 sub_sub_obj
肯定就要报错了。
所以,就有了上面这个 get_value_from_data
的函数, 它支持 get_value_from_data(obj, 'sub_obj.sub_sub_obj.key')
这样的调用方式,不管 obj 是一般的 object 还是 dict,同样也不管后面的 sub_obj 是什么类型的对象。
因为这个函数的存在,一些只是 获取数据
的地方,代码量就会急剧减少,不需要做什么额外的判断,只要调用 get_value_from_data 就可以了。当然,上面的 get_value_from_data 的示例代码并不是我提到的项目中的完整代码,完整的逻辑中,会增加一些其它的规则判断。
在 Python 中的注释语法,是以 #
开头的一行内容。
如果在 PyCharm 中,一个注释语法是类似 #todo xxxx
的, PyCharm 中有 Todo 一栏,会自动把当前项目的所有注释内容,转为一个 Todo 列表。所以,在写代码的过程中,有些需要后续再处理的,可以直接使用 Todo 进行注释,后面再完善就可以了。
但也有一个小小的经验,能不留 Todo
的就尽量不要留,但凡注释为 Todo
的,很有可能到最后都不会处理……
Editor 主区域中,选一个变量,右键点击,试试 Refactor 中选择 Rename。
除了 Refactor 之外,右键点击,另外很有用的是 Find Usages 。
这些都自己试试,就能看到效果如何,以及会发现效率的巨大提升。还有更多的小技巧,自己在使用 PyCharm 的时候,随着时间累积,慢慢也都会发现。