函数
这么重要的东西自然少不了。
那么在python中是怎么定义的呢?
def cgg(name):
print("haha %s"%name)
def是定义的意思,而cgg是函数名,name是参数。
函数的参数传递问题
传递方式
我们知道,参数传递分为值传递和引用传递,那么在python中函数的参数是怎么传递的呢?
在说参数的传递方式之前我们需要先说一下可更改(mutable)与不可更改(immutable)对象。
在python中,变量是没有类型的,只有对象才有类型,这也就是为什么我们可以对一个已经复赋10的变量赋值一个字符串。
但是对象的确是有类型的,而对象因为类型的关系分为可更改不不可更改。
比如这个例子:
a=10
b=[1,2,3]
a=20
b[0]=4
我们先来看a,赋值给a的10就是一个不可更改的对象,因为实际上我们再次赋值20的时候,我们是新建了一个20的整型对象,而原来的10已经不复存在,所以说它不可更改。
而对于b,是一个列表,我们调用b[0]=4时,它本身依然存在,只是其中一个元素发生了改变,所以列表是一个可更改对象。
那么,我们在进行函数参数传递时,这两类对象也是有差别的。
对于不可更改的对象,python会采用值传递;而对于可更改的对象,python采用引用传递。
我们就不举例了。
参数类型
python中有四类参数:必备参数、关键字参数、默认参数、不定长参数
下面我们依次讲解
必备参数
这就是我们正常的函数声明,比如上面的那个例子cgg函数就需要一个参数name,如果我们调用函数却不传递对应的参数,编译器会报错。
关键字参数
关键字参数和必备参数的区别主要是在调用上,关键字参数可以显式的给出参数的传递,因此不需要遵循函数定义时,参数列表里参数的顺序,我们举个例子:
def cgg(name,age):
print("My name is %s,and I'm %s years old."%(name,age))
cgg(age="18",name="cgg")
在这个例子中,我们可以看出name和age的顺序并没有遵循所给的顺序,而是颠倒了一下,但是并不影响程序的运行结果。
默认参数
像我们之前说的一些库函数一样,有些函数,如果我们不传入对应的参数,则会有一个默认值,同样,我们也可以在定义函数时实现这个功能:
def cgg(name,gender="男"):
print("我的名字是%s,我的性别是:%s"%(name,gender))
cgg("cgg")
cgg("xhy","女")
在cgg声明的时候,我们可以看到,在性别这个参数,我们设置了一个默认值:男性
也就是说,在没有填写这个参数时,就会默认为男性。
不定长参数
有的时候,我们的函数可能不知道对方到底要传入多少个参数,比如C语言里的printf就是一个例子。那么我们就需要一个能接受不定长参数的函数,我们可以如下定义:
def cgg(*b):
for i in b:
print(i)
cgg(10)
cgg(10,20,30)
输出结果:10 10 20 30 (换行我就略去了)
也就说,我们可以在一个参数前面加上星号,表示它是一个不定长参数,可以接收多个参数。
同时注意,像下面这个定义是被允许的:
def cgg(a,*b):
for i in b:
print(i)
cgg(10)
cgg(10,20,30)
输出结果:20 30(换行略去)
此时会把第一个参数给a,剩下的全部由b接收
但是如果我们这么做,则会有些问题:
def cgg(*b,a):
for i in b:
print(i)
cgg(10)
cgg(10,20,30)
编译了以后,会报错,而错误的原因不是不能定义a这个参数,而是说我们调用时,漏了一个参数a。
也就是说,如果不做处理,我们的参数将全部给b,而a一个也捞不到。
那么我们应该怎么办呢?
def cgg(*b,a):
for i in b:
print(i)
cgg(10,a=10)
cgg(10,20,30,a=10)
关键字参数就可以解决了。
匿名函数
这里要用到一个新的关键字,lambda。
匿名函数,顾名思义,没有名字的函数,其实与其说它是函数,不如说它是一个表达式。我们来看一个例子:
sum3=lambda a,b,c:a+b+c
print(sum3(10,20,30))
答案是60
的确很像一个加了名字的表达式,有点像……宏定义?
但是要注意一点,匿名函数是允许访问其他变量的,即使声明这个变量时还没有定义:
sum3=lambda a,b,c:a+b+c+d
d=30
print(sum3(10,20,30))
在这个示例中,匿名函数sum3定义时,我们并没有定义d这个变量,但是只要我们在调用sum3之前定义了d,这个语句就可以正常执行功能。
返回值
当然了,有了函数吗,自然少不了函数的返回值。那么这里用的依然是return。
def cgg(a,b):
return a+b
模块
简介
什么是模块?
其实我们写的那些.py文件,都是模块。
我们之前用过很多库函数自带的模块,比如time、calendar等等。
这里我们自己写一个模块:
def cgg(data):
print(data)
def cgg2(data):
print("other")
print(data)
这段代码保存在xhy.py这个模块里。
所以我们在写自己的程序时。可以如下调用:
import xhy
xhy.cgg("cgg")
运行结果:cgg
我们接下来举例会围绕这个xhy模块展开。
导入模块的方式
import
我们在开头的示例中就是一种直接import的方法,包括我们之前的学习过程中也用这种方法,我就不再举例了。
from …… import ……
字面意思很好理解,从哪里导入什么,也就说并不是整个导入模块,而是导入其中的某个部分。
比如:
from xhy import cgg
cgg("cgg")
不会出任何问题。
但是如果我们这么写:
from xhy import cgg
xhy.cgg("cgg")
cgg2("cgg")
这两个语句都是有问题的。
因为我们只导入了一个函数,所以xhy和cgg2都是未定义的。无法使用
我们还可以在import后加上星号表示导入这个模块的所有东西:
from xhy import *
cgg("cgg")
cgg2("cgg")
这样是没有任何问题的。
但是,如果我们这样写:
from xhy import *
xhy.cgg("cgg")
依然是有问题的,xhy任然未定义。
我们的模块放在哪
相信大家在新建一个模块时候都会有一个疑问,我的模块应该放在哪?
这个显然是由python会去哪里寻找模块决定的。
当我们导入一个模块时,python会去依次三个地方去找:
- 当前目录
- 如果不在当前目录,python会搜索shell变量PYTHONPATH下的每个目录
- 如果都找不到,python会选择搜索默认路径、
我们怎么知道模块里有什么?
打开py看?
未免太麻烦了。
python提供了一个函数dir():
import xhy
dir(xhy)
这样就会显示模块里会有哪些东西啦
当然了,看自己的模块恐怕没什么意思,我们也可以看官方的库:
import math
dir(math)
补充两个例子:
一个小例子:
import time
print(time.asctime())
这个可以显示现在的时间。
而这个time就是一个模块,包括之前的turtle也是一个模块。
我们能再看一个例子:
import sys
print(sys.stdin.readline())
这个一看就明白,是输出输入的东西
这里的sys也是一个模块
TIPS:readline()返回字符串
locals()和globals()
这两个函数可以在函数里调用来查看可以在当前这个函数里调用的变量和全局变量。
返回值均是字典。
reload()
对于import的语句,只会执行一次,不会因为运行而反复执行。
一般情况下的确没问题,但是如果,你中途修改了模块的内容,再执行新添加的内容,你就会发现问题了。
所以我们需要用reload函数来执行重新加载:
reload(xhy)
这样就OK了。
python中的包结构
包可以看成一个文件夹,但并不完全是文件夹的功能。
比如在python中,包就会由自己特定的结构。
python规定包必须包含一个文件,那就是__init__.py,它可以是空的,但一定要有,它用来标识这是一个包。
但是如果里面有内容,那么当这个包中有程序运行时,会先调用这个文件(从名字init也可以看出来了)