您的位置:首页 > 其它

函数 及文件读写操作,装饰器

2016-05-27 18:28 316 查看
一.函数

日常生活中,要完成一件复杂的功能,我们总是习惯把“大功能”分解为多个“小功能”以实现。在编程的世界里,“功能”可称呼为“函数”,因此“函数”其实就是一段实现了某种功能的代码,并且可以供其它代码调用。

  假设我们在编程过程中需要计算圆形的面积。如果我们不是用函数,每次需要计算原型面积的时候都需要这样

1 r1 = 12
2 r2 = 3.4
3 r3 = 3.1
4 s1 = 3.14 * r1 * r1
5 s2 = 3.14 * r2 * r2
6 s3 = 3.14 * r3 * r3


这样如果计算次数比较少还可以,如果需要计算的次数非常多,就比较麻烦了。另外如果我们要改动一下公式,比如π的值我们要改成3.141592657,有多少计算的地方就需要改多少遍,麻烦不说,还容易出错,遗漏等。函数就可以有效的解决以上的问题。

1、函数的定义及调用

  定义一个函数要使用def关键字,依次写出函数名、括号、括号中的参数和冒号:,然后用缩进的代码块写函数体,函数体内可以调用return语句返回结果,以求原型面积为例我们可以这么定义一个函数

1 def circle_area(r):
2     return 3.14 * r *r


这样如果调用这个函数的时候我们只要这样,就可以了。

circle_area(5)


这样如果我们要改动π的值只需要改动一下函数就可以了。

  另外函数名作为函数的名称,也可以像变量一样进行赋值操作、甚至作为参数进行传递,例如我们也可以把求圆形面积的函数赋值给f,然后通过f(4)这样去调用函数,意思是一样的。

1 f = circle_area
2 f(5)


2、函数的参数

  1)位置参数

  这是最常见的定义方式,一个函数可以定义任意个参数,每个参数间用逗号分割,例如:

1 def Fun(arg1, arg2):
2     print(arg1, arg2)


用这种方式定义的函数在调用的的时候也必须在函数名后的小括号里提供个数相等的值(实际参数),而且顺序必须相同,也就是说在这种调用中,形参和实参的个数必须一致,而且必须一一对应,也就是说第一个形参对应这第一个实参。例如:

>>> f = Fun
>>> f('abc',123)
#输出结果
abc 123
#如果形参和实参的数量不一致就会出现类似如下错误
>>> f('abc')
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: Fun() missing 1 required positional argument: 'arg2'
也可以通过如下方式传递参数,而不必考虑顺序问题,但数量无论如何必须一致。

>>> f(arg1="aa",arg2=123)
aa 123
>>> f(arg1="aa",arg=123)
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: Fun() got an unexpected keyword argument 'arg'

2)默认参数

  我们可以给某个参数指定一个默认值,当调用得时候,如果没有指定那个参数,那个参数的值就等于默认值

>>> def Fun2(arg1,arg2='Jason'):
...     print(arg1,arg2)
...
>>> f = Fun2
>>> f(1)
1 Jason

 >>> f('aa',456)
 aa 456

3)可变参数

  顾名思义,可变参数就是传入的参数个数是可变的,可以是1个、2个、甚至于0个

>>> def Fun3(*args):
... print(args)
...
>>> Fun3('a',2,5,100)
('a', 2, 5, 100)

说明:可以看到我们传递了三个参数都被Python转化为元祖,保存到args中了,这样我们就可以通过索引对参数记性调用,或者通过for in进行遍历

4)关键字参数

  可变参数在调用过程中会组装成元祖,元祖只能通过索引进行调用,有时候不是很方便,所以Python可以通过关键字索引将传入的参数组装成字典。

>>> def Fun4(**kwargs):
...     print(kwargs, type(kwargs))
...
>>> f = Fun4
>>> f(k1 = 'name', k2 = 'age')
{'k2': 'age', 'k1': 'name'} <class 'dict'>


关键字参数允许传入0个或任意个参数名的参数,0个的话就是一个空字典

3、参数组合

  在Python中定义函数,可以用必选参数(位置参数)、默认参数、可变参数、关键字参数这几种参数进行组合使用,但是顺序必须是,必选参数、默认参数、可变参数、关键字参数。

>>> def Fun5(arg1, arg2 = 123, *args, **kwargs):
...     print('arg1:',arg1)
...     print('arg2:',arg2)
...     print('args',args)
...     print('kwargs',kwargs)
...
>>> f = Fun5
>>> f('Jason''ss',44,'err',k1 = 234, k2 = 'abc')
arg1: Jasonss
arg2: 44
args ('err',)
kwargs {'k2': 'abc', 'k1': 234}


4、lambda匿名函数

  在Python中,提供了对匿名函数的支持,所谓匿名函数就是功能非常简单只需要一行代码就可以实现的,比如之前我们我们之前说的求圆形面积的函数,用匿名函数就可以这样定义。

>>> f = lambda r: 3.14 * r * r
>>> print(f(5))
78.5


说明:r相当于匿名函数的参数,当然也可以有多个参数,不用在写return,表达式就是返回的结果。

  使用匿名函数有个好处,疑问函数没有名字,不用担心函数名冲突,此外匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量调用该函数。

lambda表达式

学习条件运算时,对于简单的 if else 语句,可以使用三元运算来表示,即:

# 普通条件语句
if1==1:
name ='wupeiqi'
else:
name ='alex'

# 三元运算
name ='wupeiqi'if1==1else'alex'


对于简单的函数,也存在一种简便的表示方式,即:lambda表达式

# ###################### 普通函数 ######################
# 定义函数(普通方式)
deffunc(arg):
returnarg +1

# 执行函数
result =func(123)

# ###################### lambda ######################

# 定义函数(lambda表达式)
my_lambda =lambdaarg : arg +1

# 执行函数
result =my_lambda(123)


lambda存在意义就是对简单函数的简洁表示

5、关于函数的return语句

  1)函数可以没有return语句,默认没有return语句返回的就None

  2)return语句有点类似循环的break,当函数执行到return语句的时候将不会继续执行下去,直接跳出函数的执行,例如

>> def Fun6():
...     print('start')
...     return None
...     print('end')
...
>>> f = Fun6
>>> f
<function Fun6 at 0x1033ec7b8>
>>> print(Fun6())
start
None
>>> print(f())
start
None


说明:可以看到return语句后面的print('end')根本没有执行。

  3)return可以返回多个值,接受的接收的可以使用两个变量接受,也可以一个变量接收

>>> def Fun7():
...     return 'abc', 123
...
>>> r1,r2 = Fun7()
>>> print('result1',r1)
result1 abc
>>> print('result2',r2)
result2 123
>>> res = Fun7()
>>> print('res:', res)
res: ('abc', 123)


说明:可以返回多个值就是返回一个元祖,使用两个变量接收的时候回将元祖的元素与变量一一对应赋给多个变量,用一个变量接收的时候就接收了

6、关于可变参数和关键字参数的传递的小技巧

  我们已经知道可变参数和关键字参数分别将传递的参数组装成元祖和字典,那么我们同样可以直接将元祖、列表和字典直接传递给函数作为参数,传递的时候列表和元祖要变量名要在前面加一个*,字典要在之前加两个**,否则函数还是会把把它们当成一个普通的参数传递进行处理

>>> def Fun8(*args, **kwargs):
...     print(args)
...     print(kwargs)
...
>>> list = ['a',123,6,'bb']
>>> dict = {'name':'Jason','age':'20'}
>>> f = Fun8
>>> f(list,dict)
(['a', 123, 6, 'bb'], {'name': 'Jason', 'age': '20'})
{}# 可以看到两个参数都被可变参数接收了,关键字参数啥也没有

>>> f(*list, **dict) ('a', 123, 6, 'bb') {'name': 'Jason', 'age': '20'}


7、内置函数(Built-in Functions)

fp.fileno()


View Code

3.读取指定字节数据

fp.read([size])  #size为读取的长度,以byte为单位


4.读一行

fp.read([size])  #size为读取的长度,以byte为单位


5.文件每一行作为一个list的一个成员,并返回这个list。

fp.readlines([size])     #其实它的内部是通过循环调用readline()来实现的。如果提供size参数,size是表示读取内容的总长,也就是说可能只读到文件的一部分。


6.写入

fp.write(str)     #把str写到文件中,write()并不会在str后加上一个换行符


7.全部写入

fp.writelines(seq)        #把seq的内容全部写到文件中(多行一次性写入)。这个函数也只是忠实地写入,不会在每行后面加上任何东西。


8.把缓冲区的内容写入硬盘

fp.flush()


9.判断文件是否为设备文件

fp.isatty()             #文件是否是一个终端设备文件(unix系统中的)


10.获取指针位置

fp.tell()              #返回文件操作标记的当前位置,以文件的开头为原点


11.指定文件中指针位置

fp.seek(offset[,whence])


#将文件打操作标记移到offset的位置。这个offset一般是相对于文件的开头来计算的,一般为正数。但如果提供了whence参数就不一定了,whence可以为0表示从头开始计算,1表示以当前位置为原点计算。2表示以文件末尾为原点进行计算。需要注意,如果文件以a或a+的模式打开,每次进行写操作时,文件操作标记会自动返回到文件末尾。

12.返回下一行

fp.next()                #返回下一行,并将文件操作标记位移到下一行。把一个file用于for … in file这样的语句时,就是调用next()函数来实现遍历的。


13.文件裁成规定的大小

fp.truncate([size])         #把文件裁成规定的大小,默认的是裁到当前文件操作标记的位置。如果size比文件的大小还要大,依据系统的不同可能是不改变文件,也可能是用0把文件补到相应的大小,也可能是以一些随机的内容加上去。


14.指针是否可操作

seekable() 指针是否可操作


15.是否可写

writable()


4.with函数管理文件

为了避免打开文件后忘记关闭,可以通过管理上下文,即:


with open('log','r') as f:

...


如此方式,当with代码块执行完毕时,内部会自动关闭并释放文件资源。

在Python 2.7 后,with又支持同时对多个文件的上下文进行管理,即:

with open('log1') as obj1, open('log2') as obj2:
pass


操作文件

# read() # 无参数,读全部;有参数,
#                                    b,按字节
#                                    无b,按字符
# tell() 获取当前指针位置(字节)
# seek(1) 指针跳转到指定位置(字节)
# write() 写数据,b,字节;无b,字符
# close
# fileno
# flush 强刷
# readline 仅读取一行
# truncate 截断,指针为后的清空
# for循环文件对象 f = open(xxx)
# for line in f:
#     print(line)


三,装饰器

所谓装饰器decorator仅仅是一种语法糖, 可作用的对象可以是函数也可以是类, 装饰器本身是一个函数, 其主要工作方式就是将被装饰的类或者函数当作参数传递给装饰器函数。本质上,装饰器就是一个返回函数的高阶函数
假设有这么一个应用场景,一个公司的监控监控系统有很多函数用来监控不同的数据,突然有一天老大要把这么多函数,需要一个日志功能,也就是在执行函数前和执行函数后再控制台和文件中输出日志。
如果老大把这个任务交个你,你会怎么做?把每个函数都修改一遍吗?如果老大觉得日志功能太影响性能,又要去掉,怎么办?一个又一个的改回来??如果函数少需要修改的地方少还可以,如果需要修改的地方很多的话,这样显然是不明智的。用装饰器就可以很好的解决这个问题。我们先来看一下装饰器如何定义。

##装饰器
#@ + 函数名
#功能:
#1.自动执行outer函数并且将其下面的函数f1当作参数传递
#2.将outer函数的返回值,重复赋值给f1

def outer(func):
def inner(*args,**kwargs):
print("before")
r = func(*args,**kwargs)
print("after")
return r
return inner


说明:看上去和定义一个函数没多大区别,只不过在函数里有嵌套了一层函数而已。1)outer为装饰器名称,可以随意定义,就像普通函数。2)装饰器的参数func为一个函数,表示要装饰的函数。3)装饰器里面的函数名可以随意,参数的话可以根据被装饰的函数的情况而定,参数的作用只是为了把参数有重新传回被装饰的函数。通常使用*args, **kwargs为了可以匹配各种函数的参数个数等

2、装饰器的调用

调用装饰器来装饰函数,我们只需要使用Python的语法糖,在被装饰函数上方使用@装饰名的方式调用,比如我们要调用上面的outer装饰器

@outer
def f1(args):
print('aa')
print(args)
return "返回AAA"
@outer
def f2(a1,a2):
print('bb')
print(a1,a2)
return "返回BBB"
@outer
def f3():
print('100')
return "返回CCC"

# def f1():
#     print('123')
#
# def f2(func):
#     func()
#
# f2(f1)
##123
a = f1("fuck")
print(a)
b = f2("sb","qq")
print(b)
c = f3()
print(c)
##执行结果
before
aa
fuck
after
返回AAA
before
bb
sb qq
after
返回BBB
before
100
after
返回CCC


多层装饰器

User_INFO = {}

def check_login(func):
def inner(*args,**kwargs):
if User_INFO.get('is_login',None):
ret = func(*args, **kwargs)
return ret
else:
print('请登录')

return inner

def check_admin(func):
def inner(*args,**kwargs):
if User_INFO.get('user_type',None) == 2:
ret = func(*args,**kwargs)
return ret
else:
print("无权限查看")
return inner
@check_login
@check_admin
def index():
print("login success")
@check_login
def login():
User_INFO['is_login'] = 'True'
print("普通用户登录")

def search():
print("")
def main():
while True:
inp = input("1.登录2.查看3.后台登录")
if inp == '1':
User_INFO['is_login'] = True
login()
elif inp == '2':
search()
elif inp == '3':
User_INFO['user_type'] = 2
index()

main()


3、装饰器原理

其实装饰器的调用@装饰器名是一个语法糖,Python解释器在解释到这里的时候为我们做了如下工作

f1 = outer(f1)


说明:
1)调用装饰器函数将被装饰的函数作为参数传递过去。
2)这样就相当于重构了这个函数,也就是包裹了一层内容。
3)装饰器本身一个可以接收参数,但是,由于使用场合比较少,甚少会用到,这里就不讲解了,其实原理就是在定义装饰器的时候在外面再裹上一层函数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: