《Head First Python》Ch4:代码重用:函数与模块

版权声明:本文为CSDN博主「一笑照夜」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/erwugumo/article/details/91385059

1、函数

相比于C++,python中写一个函数需要一个关键字def。其主要结构如下:

def 函数名(函数参数):
"""函数文档,docstring"""
    函数体
    return

如果一个函数是一个类的一部分,它就称为一个方法。

注意函数没有类型信息,无论是函数参数还是返回值,都不需要类型信息,当然为了便于阅读,可以使用函数注解。

函数注解是py3新增的一项功能,类似注释,它们只负责提高程序的可读性,而不会对程序功能做出任何改变。

推荐python代码为了可读性,遵循PEP8的规范。以下是一个例程:

def search4vowels(word):
    """Display any vowels found in an asked-for word."""
    vowels=set('aeiou')
    found=vowels.intersection(set(word))
    for vowel in found:
        print(vowel)

效果如下:


image

可以看得出来,犯了很多愚蠢的错误。首先,最重要的是,虽然没有为函数的参数指定类型,但函数参数必须有类型,像我第一次那样,参数直接是chy是肯定不行的,单独的chy会被编译器认为是一个变量而不是一个字符串;另外,第二次没有输入参数,会报缺少参数的错误,第三次直接把函数名打错了,最后一次,加了单引号,得到正确结果。

在python中,每一个对象都有一个与之关联的真值,表示这个对象为True或者False。如果该对象计算为0、值为None、是一个空串或一个空的数据结构,那么就是False,否则是True。使用函数bool来计算对象的真值。严格来说,所有的非空数据结构都计算为True。

默认来说,函数使用return关键字来返回值,只能返回一个值,若想返回多个值,就需要使用数据结构,这和C++中类似。

将上面的函数改为返回word中的元音,如下:

def search4vowels(word):
    """Display any vowels found in an asked-for word."""
    vowels=set('aeiou')
    return vowels.intersection(set(word))

效果如下:


image

注意,当word中不含元音时,应该返回一个空集合。由于集合使用{}包围,那么空集合应该返回{}。但是返回的是set()。这是因为,{}没法区分是空集合还是空字典,因此用{}表示空字典,用set()表示空集合。

上述代码加入注解后的效果如下:

def search4vowels(word:str)->set:
    """Display any vowels found in an asked-for word."""
    vowels=set('aeiou')
    return vowels.intersection(set(word))

在运行时,加不加注解没有任何区别,只有在使用help时有区别:


image

help会把注解显示,还会把docstring也显示。

接下来,定义一个更泛用的函数,用于找到两个字符串中所有相同字符的集合,代码如下:

def search4vowels(phrase:str)->set:
    """Display any vowels found in a supplied phrase."""
    vowels=set('aeiou')
    return vowels.intersection(set(word))
 
 
def search4letters(phrase:str,letters:str)->set:
    """Return a aet of the 'letters' found in 'phrase'."""
    return set(letters).intersection(set(phrase))

可以在一个文件中定义多个函数,注意,根据PEP8,两个函数间要空两行。这里的第二个函数search4letters用于实现找到两个字符串的交集。注意,第一个函数是第二个函数在letters=‘aeiou’情况下的特例,因此,如果能够让letters默认为aeiou,那么就可以实现函数二对函数一的完全代替。这样就可以实现函数的可重用。

如何实现呢?很简单,在参数注解的后面加入默认值即可,如下:

def search4letters(phrase:str,letters:str='aeiou')->set:
    """Return a aet of the 'letters' found in 'phrase'."""
    return set(letters).intersection(set(phrase))

这样,当我们在调用search4letters函数时,若只用了一个参数,那么就默认第二个参数是aeiou;若用了两个参数,那么letters就不再是默认值。默认值与初值不同,不要搞混。默认值只有在def行才能修改。

与C++不同的是,python提供了两种参数赋值方法,位置赋值与关键字赋值。其中,位置赋值的情况下,对函数的调用与C++中相同,即函数f(A,B,C)调用时就是f(1,2,3),其中A=1,B=2,C=3。然而,关键字赋值的情况下,参数顺序就没用了,调用f可以是f(C=3,A=1,B=2),即点名某个参数等于多少。这样就不用记住参数顺序了,当然缺点就是要多打一些字。

2、模块

好,既然在1中,我们已经写好了函数,那么,如何实现它的可重用呢?

如果调用函数的代码就在函数文件里,那直接用就好了,但是如果多个文件都要用这个函数,那每个文件里都把这个函数重新写一遍,那就太蠢了。要使用import。

我们已经知道了,import是用来引入模块的,也就是说,已经有了函数,还要把它打包成一个模块,才能在其他文件中调用这个函数。

首先来看import如何引入模块。

使用import A引入一个名为A的模块,编译器会去三个地方找这个叫A的模块:首先在当前工作目录找,然后去解释器的site-package找,最后去标准库位置找。找到了的话,直接引入,没找到就报错。

当前工作目录,这个概念我是在linux中了解到的。在win中,用cmd进入命令行


image

那么这里的C:\Users\Amarantine就是当前工作目录,可以用cd来换工作目录。

如果我们import的模块不在工作目录中,那就会报错。

因此,要想使用一个模块,可以把它放在我们的当前工作目录里,但是这样太麻烦了。更好的方法是放在site-package里,这里放着的都是第三方的python模块。标准库模块不要想了,那不是我们能染指的。

在py3.4中,引入了一个新的模块,setuptools,使用这个模块可以较为方便的在site-package中加入我们自己的模块,仅需三步:

①创建一个发布描述

至少需要两个描述文件,一个是setup.py安装文件,一个是README.txt解释文件。这两个文件必须放在和函数源码文件相同的文件夹中。setup文件中的代码几乎是定死的,以发布vsearch这个模块为例,其代码如下:

from setuptools import setup
setup(
    name='vsearch',
    version='1.0',
    description='The Head First Python Search Tools',
    author='Osiris',
    author_email='123456@qq.com',
    url='baidu.com',
    py_modules=['vsearch'],
    )

重要的参数有两个,一个是name,其值是发布的包的名字;另一个是py_modules,其值是文件夹中所有py文件的列表。注意,这里py_modules即使填写错误,也不会报错。我第一次就是把vsearch打成了vresearch导致安装成功却import失败。很是蛋疼。

txt文件可以为空。

如图,文件夹中有三个文件:


image

②生成一个发布文件

这一步要使用命令行来完成。

在当前工作目录移到setup文件所在的文件夹内后,使用命令

py -3 setup.py sdist

来生成发布文件。发布文件是一个压缩包,保存在工作路径下的一个叫disk的文件夹中。

③安装发布文件

py3.4包含了一个名为pip的工具,可以用来安装包。将工作目录设置为disk后,按下图输入命令。


image

可以看出,已经成功安装。

需要验证是否安装成功,只靠上面的显示是不行的。要用import来验证。


image

如图可以看出,在我把setup文件改好之后,模块真正安装成功了。

注意,site-package实际上是一个文件夹,位于...\python\Lib\site-packages,真正安装好后,会有一个文件夹,名字中带有安装的模块,另外,模块中函数的源码也存在该文件夹中,如下:


image

如图,可以看见vsearch-1.0-py3.7.egg-info文件夹和vsearch.py文件。

3、函数的按值调用与按引用调用

在学C的时候,我们已经清楚了按值调用和按引用调用的区别,这是一个重要考点。

按值调用不会改变函数参数的值,但按引用调用会改变。

在python中,函数既支持按值调用,又支持按引用调用。也就是说,在某些情况下,函数的行为看起来像按值调用,在另外一些情况下,则是按引用调用。其本质则是因为,python中变量都是对象引用,变量中存储的是值的内存地址,而不是真正的值,从这一点看,所有的变量都是指针。

当变量指向的值是可变的值:列表、字典、集合时,按引用调用来行为;

当变量指向的值是不变的值:字符串、正数、元组时,按值调用来行为。

然而有一点要注意,参见下面两个函数:

#按值调用
def double(arg):
    print('Before: ',arg)
    arg=arg*2
    print('After: ',arg)
#按引用调用
def change(arg):
    print('Before: ',arg)
    arg.append('More data')
    print('After: ',arg)

当arg为整数时,调用double函数,参数为a,看上去a会不变,因为a是一个整数,属于不变的值,应按值调用,实际也是这样:


image

但是当arg为一个列表时,按原来的想法,参数为b,是一个列表,列表属于可变的值,因此b应该翻倍,因为是按引用调用,实际却不是这样:


image

可以看出,arg的确翻倍了,但是b没有翻倍。

这是python中=的特性决定的。

在python中,=是赋值操作,在赋值时,首先执行=右边的代码,创建一个新的对象,然后将这个新的对象赋给=左边的变量。也就是说,在上面的代码中,是对b的内容进行*2的操作,形成一个新的对象,赋给arg,然而b本身一点变化都没有。因此b不会变化。

其实还有一个好玩的事,前面说过,python中变量都是指针,那么参见下图:


image

我们将a初始化为一个整数1,b初始化为2,然后观察a和b的地址,它们是不同的;

然后将a加1,现在a=2,按C的理解,应该是140734582055568这个地址中的内容从1变成了2,但实际不是这样,是a指向了b的地址,原因是b=2。这也可以看出,对于不能改变的量,python赋值采用的是改变指向的操作完成的。这也导致了在C中很容易做的数字处理变得麻烦。

4、检查PEP8的兼容性

若想检查代码是否符合PEP8的规范,需要使用pytest框架和pep8插件。同样的,我们使用pip进行安装。

按如下执行代码:


image

如下图表示安装成功。


image

接下来按照同样方法安装pep8。

安装成功之后,前往需要检查的代码文件所在的文件夹,此处就是vsearch.py所在的文件夹,执行如下命令:

py.test --pep8 vsearch.py

效果如下:


image

可以看出,有五处不合格,三处是代码中间的冒号:后面没有加空格,一处是逗号,后面没有加空格,一处是等号=左右没有加空格。更改。然后再次检查。如下:


image

右侧的100%和1 passed说明已经没有错误。
————————————————
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容