如何设计Python代码?
我之前是学Java的,现在在学Python。到目前为止,我觉得Python有很多很酷的地方,但也很难适应的一点就是不需要声明变量的类型。我明白每个变量其实是指向一个对象的指针,但我还是不太明白该怎么设计我的代码。
举个例子,我在写一个函数,这个函数接受一个二维的NumPy数组。在函数内部,我会调用这个数组的不同方法(这个数组在NumPy中是一个array
对象)。但是将来如果我想再次使用这个函数,可能会完全忘记我应该传什么类型的参数给它。大家通常是怎么处理这个问题的呢?是写文档吗?如果是这样的话,那就需要多打字,这样一来就有点违背了不声明类型的初衷。
再比如,假设将来我想传递一个类似数组的对象。在Java中,通常会实现一个接口,然后让两个类都实现这个接口的方法。然后在函数参数中,我会把变量定义为这个接口的类型。在Python中,这个问题怎么解决呢?或者说,有什么方法可以实现同样的想法?
6 个回答
比如说,我正在写一个函数,这个函数接受一个二维的Numpy数组。在这个函数内部,我会调用这个数组的不同方法(因为在Numpy中,数组是一个对象)。但是将来如果我想用这个函数,可能会完全忘记应该传什么类型的东西给它。大家通常是怎么做的呢?他们只是写文档吗?
你需要写文档,并且给函数和变量起个合适的名字。
def func(two_d_array):
do stuff
再比如,如果将来我想传一个类似数组的对象,通常在Java中会实现一个接口,然后让两个类都实现这个接口的方法。
你可以这样做。创建一个基类,然后让多个类型从这个基类继承,这样它们就有了相同的接口。不过,很多时候这样做有点过于复杂,通常你可以直接使用鸭子类型。用鸭子类型,只要你传入的对象定义了正确的属性和方法,代码就能正常工作。
需要注意的是,你在Python中是可以检查类型的,但这通常被认为是不好的做法,因为这样会限制你使用鸭子类型和Python动态类型系统带来的其他编程模式。
我看过很多用Python写的代码,尤其是那些来自Java和.Net开发者的代码,发现了一些常见的问题,想提醒你们一下:
Python不是Java
别把所有东西都放进类里:
看起来Java开发者在写Python时,连最简单的函数都喜欢放进类里。Python不是Java。 不要写获取器和设置器,这些可以用属性装饰器来处理。
在我考虑写类之前,我有两个标准:
- 我是在把状态和功能结合起来
- 我希望有多个实例(否则用模块级别的字典和函数就可以了!)
别检查所有类型
Python使用鸭子类型。参考数据模型。它的内置类型转换会对你有帮助。
别把所有东西都放在try-except块里
只捕获你知道会出现的异常,随便用异常来控制流程会消耗计算资源,还可能隐藏bug。尽量使用你预期可能会遇到的最具体的异常。 这样能让代码在长远来看更稳健。
学习内置类型和方法,特别是:
str
join
- 只需输入
dir(str)
,学习所有方法。
list
append
(在列表末尾添加一个项目)extend
(通过添加可迭代对象中的每个项目来扩展列表)
dict
get
(提供一个默认值,避免捕获键错误!)setdefault
(根据默认值或已有值来设置!)fromkeys
(从一个可迭代的键构建一个带默认值的字典!)
set
集合包含唯一的(没有重复)可哈希对象(比如字符串和数字)。想象一下维恩图?想知道一组字符串是否在另一组字符串中,或者它们之间的交集是什么(或者不是)?
union
intersection
difference
symmetric_difference
issubset
isdisjoint
对每种你遇到的类型使用 dir()
来查看它的所有方法和属性,然后对属性使用 help() 来了解它的功能!
学习内置函数和标准库:
我见过开发者自己写最大值函数和集合对象,这有点尴尬。别让这种事发生在你身上!
在标准库中,有几个重要的模块需要了解:
os
sys
collections
itertools
pprint
(我一直在用)logging
unittest
re
(正则表达式在解析字符串时非常高效,适用于很多场景)
可以浏览文档,快速了解标准库,这里是第一部分,这是第二部分。总之,尽早浏览所有文档是个好目标。
阅读风格指南:
通过阅读风格指南,你会学到很多最佳实践!我推荐:
- PEP 8(标准库中的任何东西都是按照这个标准写的)
- 谷歌的Python风格指南
- 如果你所在的公司有风格指南,也要看看。
此外,你还可以通过在Google上搜索你关注的问题,加上“最佳实践”这个短语,找到相关的Stackoverflow回答,选择那些获得最多赞的答案!
祝你在学习Python的旅程中好运!
这是一个非常好的问题。
鸭子类型
首先要了解的是Python中的一个概念,叫做鸭子类型:
如果它走起来像鸭子,叫起来像鸭子,那我就称它为鸭子
和Java不同,Python中的类型从来不需要明确声明。无论是在编译时还是运行时,对象可以是什么类型都没有限制。
你只需要把对象当作适合你需求的类型来使用。你不需要去询问或思考它的类型。如果它实现了你想要的方法和属性,那就可以了。它就能满足你的需求。
def foo(duck):
duck.walk()
duck.quack()
这个函数唯一的要求是duck
需要有walk()
和quack()
这两个方法。下面是一个更具体的例子:
def foo(sequence):
for item in sequence:
print item
什么是sequence
? 是list
? 还是numpy的array
? 还是dict
? 还是generator
? 其实都无所谓。只要它是可迭代的(也就是说,可以用在for ... in
循环中),那它就能满足要求。
类型提示
当然,没有人愿意一直担心对象的类型不对。这个问题可以通过编码风格、约定和良好的文档来解决。例如:
- 一个名为
count
的变量应该存储一个整数 - 一个以大写字母开头的变量
Foo
应该存储一个type
(也就是class
) - 一个默认值为
False
的参数bar
,在被重写时也应该存储一个bool
类型的值
注意,鸭子类型的概念可以应用到这三个例子中:
count
可以是任何实现了+
、-
和<
的方法的对象Foo
可以是任何可调用的对象,返回一个实例bar
可以是任何实现了__nonzero__
的方法的对象
换句话说,类型从来不是明确规定的,但总是有很强的暗示。或者说,对象的能力总是被暗示,而它的确切类型并不重要。
使用未知类型的对象是非常常见的。大多数框架提供的类型看起来像列表和字典,但实际上并不是。
最后,如果你真的需要知道类型,可以查阅文档。你会发现Python的文档远远优于Java的文档。值得一读。