函数的定义,有点类似我们之前说的变量一样,也是为了便于维护,同时也提高了代码的可扩展性和重用性。函数分为有返回值的函数和没有返回值的函数(也称过程函数)。示例:
def function(): pass var = function() print(var)
输出结果是:
None
我们定义了函数function(),没有返回值,也就是说返回值数是0,这时我们在调用的时候,默认返回None
def function(): return "jasonhy" var = function() print(var)
输出结果是:
jasonhy
返回的是一个字符串,也就是说返回数是1,我们可以扩展的说,返回的就是Object
def function(): return "jasonhy",18 var = function() print(var)
输出结果是:
('jasonhy', 18)
返回的是一个字符串和一个数字,也就是说返回数大于1,我们可以扩展的说,当返回数大于1的时候,返回的是一个tuple
在以上的示例中,我们定义了一个function()函数,这个函数里面是没有参数的,但是有的函数是由参数的,比如list中添加元素的append(self, p_object)函数,p_object就是一个参数,这个参数叫做形参。参数分为形参和实参。
形参,在函数被调用的时候,才会被分配内存,当这个函数调用结束之后,内存就被释放了,所以我们说形参的作用域是在本函数内。
实参,也就是在调用函数的时候,将实际的值传给形参,这个实际的值就是实参。
案例:
def function(x,y): ''' :param x: 形参 :param y: 形参 :return: 返回x + y的和 ''' return x+y v_sum = function(10,20) #10和20是实参 print(v_sum)
在进行参数传递的时候,又可以分出两种传递形式,位置参数和关键字参数。位置参数就是我们平时正常进行参数传递形式,也就是要一一对应,多了少了都不可以;那关键字参数就是在进行值传递的时候,直接写上形参等于多少,比如x=1,示例:
def function(x,y): ''' 以关键字参数进行传递示例 :param x: 形参 :param y: 形参 :return: 返回x + y的和 ''' return x+y v_sum = function(x = 10,y=20) # x = 10,y = 20是关键字参数 print(v_sum)
有一点需要注意的就是,当位置参数和关键字参数混合使用的时候,位置参数要写在关键字参数的左边的,也是需要一一对应的。
我们经常会看到*args, **kwargs这样的参数,比如set集合中的add(self,*args, **kwargs),这样的参数我们叫做参数组。
案例:
def function(x,*args): ''' 定义一个*args的参数组函数 :param x: :param args: 参数组 :return: 返回的也是一个和 ''' v_sum = 0 for item in args: v_sum = v_sum + item print("输出的x为%d"%x) #测试哪些值传递了x print("输出的args为%s"%str(args)) #测试哪些值传给了args return x + v_sum v_s = function(1,2,3,4,5) print(v_s)
输出结果是:
输出的x为1
输出的args为(2, 3, 4, 5)
15
从输出的结果,我们发现,第一个实参传递给了x,剩余的以元组的形式传递给args,也就是一种位置参数的形式
def function(x,**kwargs): ''' 定义了一个kwargs参数组的函数 :param x: :param kwargs: 参数组 :return: ''' print(x) #测试x获取的值 print(kwargs) #测试kwargs获取的值 function(1,y=2,z=3)
输出结果是:
1
{'y': 2, 'z': 3}
从输出的结果,我们可以看出,第一个参数传递给了x,也就是位置参数,剩下的两个参数以字典的形式传递给了kwargs,所以说kwargs是关键字参数
全局变量和局部变量:
全局变量是定义在函数外面的值,作用域是在本程序中都有效,局部变量是定义在函数里面的值,作用域是在本函数中,一般情况我们都是全局变量用大写,局部变量用小写,比如:
A = 1 B = 2 def add(): ''' add()函数里定义了两个变量 :return: ''' a = 1 b = 2 return a + b def sub(): ''' sub()直接返回两个全局变量的差值 :return: ''' return B - A print(add()) print(sub())
嵌套函数: 就是在函数内定义函数,而函数的作用域在定义的时候就已经固定了,不会随着调用位置的改变而改变
USER_NAME = "Jasonhy" def get_name(): ''' 定义了一个全局变量,然后将全局变量赋值给局部变量 :return: 将内层函数名返回 ''' name = USER_NAME def print_name(): print("print_name()函数:",name) return print_name name = get_name() # 返回的是内层函数的地址,我们通过地址就可以直接调用内层函数 print(name) # 输出的结果是:<function get_name. <locals>.print_name at 0x0065E858> name() # 输出结果是:print_name()函数: Jasonhy
递归: 就是函数内部自己调用自己,具有以下特性:
①必须有一个明确的结束条件,如果没有明确的结束条件的话,整个函数将会进入一个不断的递归调用中,进入了一个死循环
②每
递归一次,问题规模都会减少,向结束条件靠拢
③递归的效率不高,因为要重复的调用,知道条件满足,很耗性能,如果递归的次数过多,还容易造成栈溢出
案例:
LIST_NUM = [11,33,44,45,66,78,88] def find_num(list,num): print(list) if len(list) > 1: mid = int(len(list) / 2) # 如果列表的长度大于1,将列表的长度一般len赋给中间变量mid if list[mid] == num: print("找到数字了",list[mid]) elif list[mid] > num: # 说明找到的数字在左边 print("\033[31;1m找到的数在%s左边\033[0m" % list[mid]) return find_num(list[0:mid],num) # 从左边开始找 else: # 找到的数在mid的右边 print("\033[31;1m找到的数在%s右边\033[0m" % list[mid]) return find_num(list[mid+1:],num) else: if list[0] == num: print("找到数字了",list[0]) else: print("要找的数字不在列表中!!!") find_num(LIST_NUM,44)
匿名函数: 就是没有函数名的函数,定义的格式如下:
lambda 参数 : 返回值
匿名函数一般是和其他函数结合使用
案例:
# 找出最大值,一般情况我们通过for的来遍历value值,这里我们可以通过匿名函数来完成 AGE_DICT = {"a_age":18,"b_age":25,"c_age":20} max_age = max(AGE_DICT,key = lambda k : AGE_DICT[k]) print(max_age,AGE_DICT[max_age])
函数式编程: 在语言编程中,有面向过程,函数式,和面向对象三种,面向过程简单的说,就是做一件事需要自己一步步去安排,而面向对象就是自己不需要知道过程是怎么进行的,需要的时候直接去调用就行了,函数式呢?就是我们定义的编程函数加上数学意义的函数,它的特性如下:
①不可变,不用变量保存状态,不能修改变量
# 非函数式 NUM = 1 def non_functional(): global NUM NUM += 1 return NUM non_functional() print("非函数式",NUM) # 函数式 def functional(): return NUM + 1 functional() print("函数式",NUM)
②第一类对象,函数即"变量"
高阶函数:函数接收的参数是一个函数名;返回值包含函数名
③尾调用,也就是在函数的最后一步调用另一个函数,我们需要注意的是,最后一行不一定是最后一步,需要补充的是,关于尾调用的优化就是在最后一步的时候进行递归
def set_name(name): print("通过get_name函数被调用了") return name def get_name(name): ''' 如果在这个函数中,给set_name()赋一个变量x后,再将x返回,那么就不是尾调用了, 因为set_name()不是在最后一步调用;另外如果返回时set_name() + "= 18",也不是 尾调用了,因为也不是最后一步调用了set_name(),先进行+运算,然后再将值返回 :param name: :return: ''' return set_name(name) get_name("Jasonhy") def set_sex(sex): print("通过get_msg函数调用了set_sex") return sex def set_age(age): print("通过get_msg函数调用了set_age") return age def get_msg(type_msg): ''' 在if和elif中,哪个都有可能成为最后一步,但是在这个函数中的最后一行是elif下面的 返回值 :param type_msg: :return: ''' if type(type_msg) == str: return set_sex(type_msg) elif type(type_msg) == int: return set_age(type_msg) get_msg(18)
map()函数:主要是用来修改列表的元素,而且每个元素的顺序不改变,在map函数中,第一个参数是处理方法,一般情况我们可以直接用匿名函数来完成,第二个参数是可迭代对象,得到的结果是一个可迭代对象
LIST_NUM = [1,2,3,4,5] print(list(map((lambda x : x + 1),LIST_NUM))) # 输出结果是:[2, 3, 4, 5, 6]
filter()函数:主要用于遍历序列中的每个元素,判断每个元素的布尔值,如果是True则留下
LIST_STR = ["ours","yours","theirs","we","you","they"] print(list(filter((lambda x : not x.endswith("rs")),LIST_STR))) # 需要的没有rs的 输出结果是:['we', 'you', 'they']
reduce()函数:主要用来对序列的合并
from functools import reduce LIST_NUM = [1,2,3,4,5] print(reduce((lambda x,y:x + y),LIST_NUM)) # 输出结果是15
内置函数:
案例一:数学运算
print(abs(-2)) # 求绝对值 输出结果是: 2 print(complex(12,3)) # 创建一个复数,第一个参数是实数部分,第二个参数是虚数部分 输出结果是: 12 + 3j print(divmod(10,3)) # 取商和模 返回的是元组类型的,第一个元素是商,第二个元素是模 输出结果是:(3,1) print(float("1")) # 将一个数字字符串或者数字变成浮点数 输出结果是:1.0 print(int(2.3)) # 将一个字符串数字或者数字变成整型 输出结果是:2 print(pow(2,3,3)) # 有两种类型,如果只有两个参数x和y,则返回的结果是x的y次幂,如果有三个参数x,y和z,则返回的结果是x的y次幂,然后对z取模 输出结果是:2 print(list(range(5))) # 返回一个序列,默认从0开始 输出结果是:[0, 1, 2, 3, 4] print(round(4.6456878412,3)) # 四舍五入 第二个参数表示保留几位小数 输出结果是:4.646 print(sum([1,2,3,4,5])) # 对一个序列进行求和 输出结果是:15 print(oct(10)) # 将十进制变成八进制 输出结果是:0o12 print(hex(10)) # 将十进制变成十六进制 输出结果是:0xa print(bin(10)) # 将十进制变成二进制 输出结果是:0b1010 print(chr(65)) # 返回一个整数对应的ASCII字符 输出结果是:A
案例二:集合类操作
# 格式化输出字符串,格式化的参数顺序从0开始 # 在讲内置format()函数之前,我们先来看str的format()方法 # 通过位置来映射 print("{0},{1}".format("Jasonhy",18)) print("{},{}".format("Jasonhy",18)) print("{0},{1},{0}".format("Jaosnhy",18)) # 输出结果是:Jaosnhy,18,Jaosnhy # 通过关键字参数 print("{name},{age}".format(name = "Jasonhy",age = 18)) # 通过对象属性 class Person: def __init__(self,name,age): self.name,self.age=name,age def msg(self): return "My name is {self.name},is {self.age} old".format(self=self) p = Person("Jasonhy",18) print(p.msg()) # 输出结果是:My name is Jasonhy,is 18 old # 通过下标 p = ["Jasonhy",18] print("{0[0]},{0[1]}".format(p)) # 填充与对齐 # ^ --居中, < -- 左对齐, > -- 右对齐 :号后面带填充的字符,只能是一个字符,不指定的话默认用空格填充 print("{:>8}".format("187")) print("{:0>8}".format("187")) print("{:a^8}".format("187")) # 输出结果是:aa187aaa # 精度与类型f # 表示留两位小数 print("{:.2f}".format(3.1415923)) # 进制 b,d,o,x分别表示二进制,十进制,八进制,十六进制 print("{:b}".format(10)) print("{:o}".format(10)) print("{:x}".format(10)) # 可以用逗号来做金额千位隔开 print("{:,}".format(12545112131)) # 输出结果是:12,545,112,131 # format()内置函数 f = format(1236) print(type(f),f) # 将数字格式成字符串 输出结果是:<class 'str'> 1236 print(format(10,"X")) # 将十进制变成十六进制 输出结果是:A print(format(3.14,"0=10")) # 左边填充 输出结果是:0000003.14 print(format(3.1415923,"05.2")) # 有效位是2位,一共有5位,不够在前面补0 输出结果是:003.1 print(format("Jasonhy","*<10")) # 左对齐10,不够以*填充,默认是空格 输出结果是:Jasonhy***
e = enumerate("Jasonhy") # 将一个序列变成索引序列 如果调用next()方法,返回的是一个元组,第一个元素是索引,第二个元素是对应的值 for index,value in e: print(index,value,end=",") # 输出结果是:0 J,1 a,2 s,3 o,4 n,5 h,6 y, i = iter("Jasonhy") # 生成一个迭代器 print(i.__next__()) # 输出结果是:J print(max("456123")) # 返回序列中的最大值 print(min("456123")) # 返回序列中的最小值 print(dict(name="Jasonhy",age="18")) # 以键对的方式创建一个字典 输出结果是:{'name': 'Jasonhy', 'age': '18'} print(dict(zip(["name","age"],["Jasonhy",18]))) # 以映射函数的方式来构建字典 print(dict([("name","Jasonhy"),("age",18)])) # 以可迭代对象方式来构建字典 print(sorted([4,5,1,3,2,6,8])) # 排序
实例三:逻辑判断
l = ["12","",None,465] print(all(l)) # 参数是一个序列,然后遍历序列中的每个元素,如果每个元素都为真,则返回真,否则返回False 输出结果是:False print(any(l)) # 与all相反,只要有一个为True,那么返回就是True 输出结果是:True
实例四:反射
# callable() 检查对象object是否可以调用,如果返回True,object仍有可能调用失败,但是如果返回False,调用object一定不会成功 print(callable(str)) # 输出结果是True print(callable("str")) # 输出结果是False print(callable(list)) # 输出结果是True print(callable([1,2,3])) # 输出结果是False # 如果类中声明了__call__()方法,那就可以调用了 class People: def __call__(self, *args, **kwargs): return 0 p = People() print(callable(p)) # 输出结果是:True # classmethod 注解,用来说明这个方法是类方法,类似Java中的static,可以被类调用,也可以被类的实例调用,类方法不需要self参数 class Animal: @classmethod def name(cls,*args,**kwargs): return 0 a = Animal() print(a.name()) print(Animal.name()) # compile()将source编译为代码或者AST对象,代码对象能够通过exec语句来执行或者eval进行求值 # filename:代码文件名称,如果不是从文件读取代码则传递一些课辨认的值 # model:指定编译代码种类,可以自定"exec","eval","single",如果是exec类型,表示一个序列语句,可以进行运行,如果是一个eval,表示是一个单一的表达式语句,可以用来计算相应的值出来,如果是single,表示是一个单一的语句,采用交互模式执行,在这种情况下,如果是一个表示式,一般输出为结果,而不会是以None输出 v_str = "for item in range(5):print(item,end=',')" c = compile(v_str,"","exec") # 编译字节码对象 exec(c) # 执行 输出结果是:0,1,2,3,4, v_str = "1*2 + 2*3" c = compile(v_str,"","eval") print(eval(c)) # 列出某个类型的可用方法 print(dir(str)) # 输出结果是:['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill'] class People: def __init__(self): self.name = "Jasonhy" self.age = 18 def printAttr(self): print("属性有name[%s]和age[%s]"%(self.name,self.age)) p = People() p.printAttr() # 获取属性值 print(getattr(p,"name","not find")) # 如果有这个属性,获取属性的值 否则是not find # 判断属性值是否存在 print(hasattr(p,"name")) # 给属性赋值 setattr(p,"age",20) print(getattr(p,"age","not find")) # 删除属性 delattr(p,"age") print(hasattr(p,"age")) # 输出结果是False # 如果对象为哈希表类型,返回一个哈希值 print(hash(p)) # 返回对象的唯一标识 print(id(p)) # # 逻辑判断object是否是class实例 print(isinstance(p,People)) # 输出结果是:True class Man(People): def __str__(self): return 0 def printRes(self): print('我是People的子类') print(issubclass(Man,People)) # 输出结果是True # 返回内存镜像类型的对象 v = memoryview(b'abcd') print(list(v)) # 输出结果是:[97, 98, 99, 100] # 属性访问的包装类 class Test: def __init__(self): self._test = None def getTest(self): return self._test def setTest(self,value): self._test = value tee = property(getTest,setTest) test = Test() test.tee = "测试用例" print(test.tee) print(repr("Jasonhy\t")) # 将一个对象变成可打印格式 输出结果是:'Jasonhy\t' class TestStatic: def __init__(self): self._test = "test" # 声明一个方法为静态方法,可以通过类名直接调用 @staticmethod def printRes(): print("声明静态方法") TestStatic.printRes() # 返回对象变量 print(vars(People)) # bytearray 返回一个byte数组 # 有三个参数,source,encoding,error # 如果source为字符串,encoding必须提供 # 如果source为整数,返回这个整数所指定的长度的空字节数组,整数必须大于0 # 如果source参数实现了buffer接口的object对象,那么将使用只读的方式将字节读到字节数组后返回 # 如果source参数一个可迭代对象,那么这个迭代对象的元素都必须符合0 <= x < 256以便初始化到数组中 b = bytearray("Jasonhy",encoding="utf") # 报错TypeError: string argument without an encoding print(b) b = bytearray(1) print(b) # 输出结果是: bytearray(b'\x00') b = bytearray([1,2,3]) print(b) b = bytearray([456,2]) # 报错: ValueError: byte must be in range(0, 256) print(b) # zip 合并列表,将对象中对应的元素合并成一个元组,然后返回由这些元组组成的列表,若传入的参数不等,则返回长度相同对象最短的,可以利用*来解压list a = [1,2] b = [3,4] z = zip(a,b) l = list(z) print(l) # 输出结果是:[(1, 3), (2, 4)] z = zip(*l) print(list(z)) # 输出结果是:[(1, 2), (3, 4)]