python函数与作用域

##函数
– 函数
— 数学定义:y=f(x),y是x的函数,x是自变量
— python函数:由若干语句组成的语句块、函数名称、参数列表构成,它是组织代码的最小单元;完成一定的功能

– 函数的作用
— 结构化编程对代码的最基本的封装,一般按照功能组织一段代码
— 封装的目的是为了复用,减少冗余代码
— 代码简洁,美观

– 函数的分类
— 内建函数,如min()
— 库函数,如math.ceil()等

##函数定义、调用
– def语句定义函数

def函数名(参数列表)
函数体(代码块)
[return 返回值]

— 函数名就是标识符,命名要求一样
— 语句块必须缩进,约定4个空格
— Python的函数没有return语句,隐式会返回一个None值
— 定义中的参数列表成为形式参数,只是一种符号表达,简称形参

– 调用
— 函数定义,只是声明了一个函数,它不会被执行,需要调用
— 调用的方式,就是函数名加上小括号,括号内写上参数
— 调用时写的参数是实际参数,是传入的值,成为实参

##函数参数
– 参数调用时传入的参数要和定义的个数相匹配(可变参数例外)

– 位置参数
— def f(x,y,z) 调用使用f(1,3,5)
— 按照参数定义顺序传入参数

– 关键字参数
— def f(x,y,z)调用使用f(x=1,y=3,z=5)
— 使用形参的名字来出入实参的方式,如果使用了形参名字,那么传参就可以和定义顺序不同
— 传参 f(z=None,y=10,x=[1]) 要求位置参数必须在关键字参数之前传入,位置参数是按位置对应的

##函数参数默认值
— 定义时,在形参后面跟上一个值
def add(x=4,y=5)
return x+y
— 作用 参数的默认值可以在未传入足够的实参的时候,对没有给定的参数赋默认值

def login(host=’127.0.0.1′,port=’8080′,username=’wayne’,passwor=’magedu’):
print(‘{}:{}@{}/{}’.format(host, port, username, password))
login()
login(‘127.0.0.1’, 80, ‘tom’, ‘tom’)
login(port=80, password=’magedu’, host=’www’)

##可变参数
– 可变参数
— 一个参数可以匹配任意个参数

– 位置参数的可变参数
— 在形参前使用*表示该形参是可变参数,可以接收多个实参
— 收集多个实参为一个tuple

– 关键字参数的可变参数
— 配置信息打印

def showconfig(**kwargs):
for k,v in kwargs.items():
print(‘{}={}’.format(k,v))
showconfig(host=’127.0.0.1′,port=’8080′,username=’wayne’,password=’magedu’)

— 形参前使用**符号,表示可以接收多个关键字参数
— 收集的实参名称和值组成一个字典

– 总结
— 有位置可变参数和关键字可变参数
— 位置可变参数在形参前使用一个*
— 关键字可变参数在形参前使用两个星号**
— 位置可变参数和关键字可变参数都可以收集若干个实参,位置可变参数收集形成一个tuple,关键字可变参数收集形成一个dict
— 混合使用参数的时候,可变参数要放到参数列表的最后,普通参数需要放到参数列表前面,位置可变参数需要在关键字可变参数之前

-举例

def fn(x,y,*args,**kwargs):
print(x)
print(y)
print(args)
print(kwargs)
fn(1,2,3,4,5,a=1,b=”abc”)
fn(3,5)
fn(3,2,a=1,b=’abc’)
fn(2,3,x=5,y=6,a=1,b=”asf”) #由于x,y重复赋值 错误

def fn(*args, x, y, **kwargs):
print(x)
print(y)
print(args)
print(kwargs)
fn(3,5) #错误
fn(3,4,5) #错误
fn(3,5,a=1,b=’python’) #错误
fn(7,9,y=5,x=3,a=1,b=’python’) #正确

– keyword-only参数
— 如果在一个星号参数后,或者一个位置可变参数后,出现的普通参数,实际上已经不是普通的参数了,而是keyword-only参数

def fn(*args, x):
print(x)
print(args)
fn(3,5) #错误 缺少x
fn(3,5,7) #错误 缺少x
fn(3,5,x=7) #正确
— 出现def fn(**kwargs,x) 这个情况是错误的。

– keyword-only 另一种形式
— def fn(*,x,y) ‘*’之后的普通参数都变成必须给的keyword-only参数

##函数参数
– 参数规则
— 参数列表一般顺序是,普通参数,缺省参数,可变位置参数,keyword-only参数,可变关键字参数

def fn(x, y, z=3, *arg, m=4, n, **kwargs):
print(x,y,z,m,n)
print(args)
print(kwargs)

##参数解构
– 给参数提供实参的时候,可以在集合类型前使用*或者**,把集合类型的结构解开,提取出所有元素作为函数的实参
– 非字典类型中使用*解构成位置参数
– 字典类型使用**解构成关键字参数
– 提取出来的元素数目要和参数的要求匹配,也要和参数的类型匹配

##练习
-编写一个函数能够接收至少2个参数,返回最小值和最大值

import random
def show(*nums):
print(nums)
return max(nums),min(nums)
print(*show(*[random.randint(1,100) for _ in range(5)]))

-编写一个函数,接收一个参数n,n为正整数,两种打印方式,必须对齐。
上三角

def show(n): #法一
tail = ‘ ‘.join([str(i) for i in range(n,0,-1)])
width = len(tail)
for i in range(1,n):
print(“{:>{}}”.format(” “.join([str(j) for j in range(i,0,-1)]),width))
print(tail)
show(12)

def fn(n): #法二
for i in range(1,n+1):
for j in range(n,0,-1):
if i >= j:
print(‘{}’.format(str(j)),end=” “)
else:
print(‘{}’.format(‘ ‘*len(str(j))),end=” “)
print()
fn(12)

下三角

def showtail(n): #法一
tail = ” “.join([str(i) for i in range(n,0,-1)])
print(tail)
for i in range(len(tail)):
if tail[i] == ‘ ‘:
print(‘ ‘*i,tail[i+1:])
showtail(12)

def fn(n): #法二
for i in range(n,0,-1):
for j in range(n,0,-1):
if i >= j:
print(‘{}’.format(str(j)),end=” “)
else:
print(‘{}’.format(‘ ‘*len(str(j))),end=” “)
print()
fn(12)

##插入排序
– 原理
— 在未排序序列中,构建一个子排序序列,直至全部数据排序完成;将待排序的数,插入到已经排序的序列中合适的位置;增加一个哨兵,放入待比较的值,让它和后面已经排好的序列比较,找到合适的插入点

– 性能
— 最好的情况,n-1次;最差的情况n(n-1)/2;
— 使用两层嵌套循环,时间复杂度O(n^2)
— 稳定排序算法
— 使用在小规模的数据比较
— 可以用二分法来优化

实现

lst = [1,5,2,9,7,8,4,6,3]
lst = [0] + lst
for i in range(2,len(lst)):
lst[0] = lst[i]
j = i – 1
if lst[j] > lst[0]:
while lst[j] > lst[0]:
lst[j+1] = lst[j]
j -= 1
lst[j+1] = lst[0]
print(lst[1:])

##函数返回值
– python函数使用return语句返回“返回值”
– 所有函数都有返回值,如果没有return语句,隐式调用return None
– return语句不一定是函数的语句块的最后一条语句
– 一个函数可以存在多个return语句,但是只有一条可以被执行。
– return None可以简写成return
– 如果函数执行return语句,函数就会返回,当前被执行的return语句之后的其它语句就不会被执行了
– 作用:结束函数调用、返回值
– return [1,3,5] 是指返回了一个列表,是一个列表对象
– return 1,3,5 其实是被python封装成了一个元组

## 解构
def showlist():
return 1,2,3
x,y,z = showlist() #使用解构提取更为方便

## 举例
def fn(x):
for i in range(x):
if i > 3:
return i
else:
print(‘{} is not greater than 3′.format(x))
# print(fn(5)) 结果返回了4
# print(fn(3)) 结果是3 is not greater than 3

##函数嵌套
– 在一个函数中定义了另外一个函数
##
def outer():
def inner(): #内部函数不能被外部直接使用,会抛出NameError异常
print(“inner”)
print(“outer”)
inner()
outer()
inner()

##作用域
– 一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域
– 全局作用域:整个程序运行环境中都可见
– 局部作用域:在函数、类等内部可见,局部变量使用范围不能超过其所在的局部作用域

##
def outer1():
o = 65
def inner():
print(“inner {}”.format(o)) #此处的o调用outer1函数里的o
print(chr(o))
print(“outer {}”.format(o))
inner()
outer1()
def outer2(): #
o = 65
def inner():
o = 97 #这个o是重新定义的,没有覆盖上面的o
print(“inner {}”.format(o)) #此处的o调用本地o的值97
print(chr(o))
print(“outer {}”.format(o))
inner()
outer2()

**错误举例**
![](leanote://file/getImage?fileId=59deda9e1b524f2c5b000001)
– 它相当于在foo函数内部定义了局部变量x,所以foo内部所有x都是这个局部变量x。但是这个x还没有被完成赋值,就做加1操作。发生错误

**改变方法:global**
##
x=5
def foo():
global x #全局变量
x = 10 #赋值即定义,把x重新变成10
x += 1
print(x) #输出11
print(x)

##global使用原则
– 外部作用域变量会内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离
– 如果函数需要使用外部全局变量,请使用函数的形参传参解决

##闭包
– 自由变量:未在本地作用域中定义的变量。例如定义在内层函数外的外层函数的作用域中的变量
– 闭包:内层函数引用到了外层函数的自由变量,就形成了闭包。
##例子
def counter():
c = [0]
def inc():
c[0] += 1 #c已经定义,inc中的使用方式为c的元素修改值
return c[0]
return inc
foo = counter()
print(foo(),foo()) # 打印 1 2
c = 100
print(foo()) # 打印 3 它引用的是inc中的变量c

##**nonlocal关键字**
– 使用nonlocal关键字,将变量标记为在上级的局部作用域中定义,但不能全局中定义
##举例
def counter():
count = 0
def inc():
nonlocal count #它声明count变量在上一级作用域中
count += 1 #形成了闭包
return count
return inc
foo = counter()
foo()
foo()

##默认值的作用域
##举例
def foo(xyz = []):
xyz.append(1)
print(xyz)
foo() # [1]
foo() # [1,1]
print(xyz) #报错,xyz没有定义
#为什么打出[1,1]因为,函数也是对象,python把函数的默认值放在了属性中,这个属性就伴随着这个函数对象的整个生命周期。查看foo.__defaults__

## 默认值引用类型
def foo(xyz=[], u=’abc’, z=123):
xyz.append(1) # xyz默认值是引用类型,元组不会变化。
return xyz
print(foo(), id(foo))
print(foo.__defaults__)
print(foo(), id(foo))
print(foo.__defaults__)

## 默认值的作用域
– 可变类型默认值,如果使用默认值,就可能修改这个默认值
– 有时候这个特性好,但是有副作用
##解决方法两种
def foo(xyz=[], u=’abc’, z=123):
xyz = xyz[:] # 影子拷贝
xyz.append(1) # xyz都是传入参数或者默认参数副本,不能修改原参数
print(xyz)
foo()
print(foo.__defaults__)
foo()
print(foo.__defaults__)
foo([10])
print(foo.__defaults__)
foo([10,5])
print(foo.__defaults__)
(2)
def foo(xyz=None, u=’abc’, z=123):
if xyz is None: #使用不可变类型的默认值
xyz = [] # 若缺省值None就创建一个列表
xyz.append(1) #如果传入一个列表,就修改这个列表
print(xyz)

##函数的销毁
### 全局销毁
– 重新定义同名函数
– del语句销毁函数对象
– 程序结束时
### 局部销毁
– 重新再上级作用域定义同名函数
– del语句删除
– 上级作用域销毁时

 

 

 

 

 

 

 

 

 

 

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/87921

(0)
上一篇 2017-10-17 00:45
下一篇 2017-10-17 09:15

相关推荐

  • 面向对象之魔术方法

    特殊属性 属性 含义 __name__ 类、函数、方法的名称 __module__ 类定义所在的模块名 __class__ 对象或类所属的类 __bases__ 类的基类的元组,顺序为它们在基类列表中出现的顺序 __doc__ 类、函数的文档字符串,若没定义则为None __mro__ 类的mro,方法查找顺序 __dict__ 类或实例的属性,可写的字典 …

    2018-05-20
  • Python递归函数、生成器以及匿名函数

    Python递归函数、生成器以及匿名函数

    2018-04-15
  • Python入门之迭代,列表解析,生成器及文件操作

    迭代器是访问集合元素的一种方式,迭代器包含了__iter__()(返回迭代器对象本身)next()(返回迭代器的下一个元素)迭代器只能往前不会退后 以下类型支持迭代 序列类型:list,str,tuple 非序列类型:dict,file 自定义类 通过内建的函数iter()或对象自带方法__iter__可以获取迭代器对象 l1=range(5) iter1=…

    Linux干货 2015-12-13
  • Python模块及详解(2)

    目录: 一、报表之Excel操作XlsxWriter模块 二、Python与rrdtool的结合模块 三、构建集中式的病毒扫描机制 四、系统批量运维管理器paramiko详解 五、系统批量运维管理器Fabric详解 一、报表之Excel操作XlsxWriter模块 Excel是当今最流行的电子表格处理软件,支持丰富的计算函数及图表,在系统运营方面广泛用于运营…

    2018-01-17
  • 滑动窗口

    数据载入 def load(path:str): with open(path) as f: for line in f: tmp = extract(line) if tmp: yield tmp else: # TODO 解析失败就抛弃,或者打印日志 continue 时间窗口分析 概念 很多数据,例如日志,都和时间相关的,都是按照时间顺序产生的。 产生…

    2017-11-04