Python 学习手册——第19章 函数与类
一、函数
1、函数的创建定义、调用
def <name>(arg1, arg2, arg3...)
函数主体往往都包含一条 return 语句,返回至函数调用处
# 定义一个乘法 def times(x, y): return x * y print(times(3, 4)) #12 print(times("abc ", 3)) #abc abc abc # 查找字符串公共字符 def Intrsect(seq1, seq2): res = [] for x in seq1: if x in seq2: res.append(x) return res print(Intrsect("yang", "gai")) #['a', 'g']
2、函数的多态
# 查找字符串公共字符 def Intrsect(seq1, seq2): res = [] for x in seq1: if x in seq2: res.append(x) return res # python中的函数的多态 print(Intrsect("yang", "gai")) #['a', 'g'] print(Intrsect([1, 2, 4], (1,4))) print(Intrsect("yang", ("yang", "zzz"))) ''' ['a', 'g'] [1, 4] [] '''
3、函数变量
- 本地变量
在函数内部进行赋值的变量名都默认为本地变量,只在函数中可见,且仅仅在函数调用运行时存在,函数退出运行时消失完整的介绍在第十七章。
- 函数变量作用域
python 创建、改变或者查找变量名都是在相应的命名空间中进行的。变量作用域就是其命名空间或者说其使用、访问范围。变量被赋值的地方决定了这个变量的命名空间,在此空间外该变量是不可见的;函数内的变量名都是与函数的命名空间相关联,也就有:在函数内定义的变量只能在函数内部使用,不能在外部使用。除此以外,函数外部的变量名和函数内部变量名不冲突,是两个不同的变量。一个出现在嵌套函数中的变量对于被嵌套函数来说是非本地变量。
- 嵌套作用域
x = 9 def f1(): x = 8 def f2(): print(x) f2() f1() # 8 def f1(): x = 8 def f2(): print(x) return f2 ac = f1() # return f2 function ac() # f2 函数的调用是在 f1 运行后进行的,但是 f2仍然记住了在f1嵌套作用域的x def maker(n): def action(x): return x**n return action f = maker(3) print(f) print(f(4)) ''' <function maker.<locals>.action at 0x0000020511501488> '''
在def 之外赋值的变量,是文件的全局变量;赋值的变量名除非生命为全局变量,否则均为本地变量;在原处修改对象不会将变量划分为本地变量。
global 语句是一个命名空间的声明,告诉python文件打算生成一个全局变量。
x = 8 def modify(): global x x = 9 modify() print(x) # 9
nonlocal 语句作用和 global 语句相似,这里不详述。
4、函数参数
- 参数与共享引用
对于函数调用,如果参数被赋值了不可变对象,则对对象的的修改不会影响到调用者;反之若是参数赋值了可变对象,则对对象的修改会影响到调用者。
def change(a, b): a = 2 b[1] = 3 x = 1 y = [0, 1.1] change(x, y) print(x, " ", y) #1 [0, 3] # x 未发生改变, y改变了
- 参数匹配语法
def func(**name) 匹配并收集(在元组中)所有包含位置的参数
def func_1(**args): print("type = {}".format(type(args))) print("content = {}".format(str(args))) func_1(name = "yang", age = 20, birthday = 1999) ''' type = <class 'dict'> content = {'name': 'yang', 'age': 21, 'birth': 1999} '''
def func(*name) 匹配并收集(在字典中)所有包含位置的参数
- 匿名函数: lambda
def func_1(x, y, z): return x+y+z print(func_1(0, 1, 2)) # 3 func_2 = lambda x, y, z: x + y + z print(func_2(1, 2, 3)) # 6 ''' type = <class 'dict'> content = {'name': 'yang', 'age': 21, 'birth': 1999} ''' print(func_1.__class__) print(func_1.__dir__) print(func_1.__name__) ''' <class 'function'> <built-in method __dir__ of function object at 0x000001FAA0471400> func_1 '''
二、类
类包括类的属性和方法,类也是模块内的属性。
1、类的创建与编写
class People(object): def __init__(self,name, age): self.name = name self.age = age def print(self): # self不可省略,传递的是一个实例对象参数 print("{}: {}".format(self.name, self.age)) p1 = People("Ming", 21) p1.print()
def func(self, args): 中的self是不可缺省的,虽然相当于Java中的this。
__init__ 方法
该方法是创建类实例时自动调用执行的,在该方法内传入的参数都会成为新实例的属性,效果就是在创建实例时初始化了实例,而不需要上面那样额外的方法调用。也被称作构造函数
2、类的特征
- 类的继承
子类到父类或者超类的继承搜索会从实例往上进行,到子类,再到超类或者父类,直到首次出现所找得到父类为止。如果子类需要对父类的方法进行修改,只需重新定义方法就会覆盖父类的方法,亦即重载。
子类继承的超类名称要列在类开头的括号中;子类可以继承一个以及一个以上的父类。
- 运算符重载
运算符重载就是让用类写成的对象,可以像内置类型对象那样进行一些常规运算:加法、切片、打印...。类通过对一些特殊命名的方法的重写实现了预算法的重载,当实例出现在内置的运算中时,该特殊命名的方法便会自动调用。如对于内置运算 + 的特殊命名方法为 __add__ ,当对象出现在 + 表达式中时,该方法就会自动调用。类似的还有 __sub__ 覆盖 - 运算。
class People(object): def __init__(self, name, age): self.name = name self.age = age # __gt__ 重载 < 运算符 def __gt__(self, other): if(self.age < other.age): print("{} age is less than {}".format(self.name, other.name)) else: print("{} age is bigger than {}".format(self.name, other.name)) class Student(People): def setnumber(self, num): self.number = num p1 = People("Ming", 12) p3 = People("Wang", 13) p2 = Student("Gang", 21) print(p1.age, p1.name) print(p2.name, p2.age) print(p1 > p3) ''' 12 Ming Gang 21 Ming age is less than Wang None '''
该语句目的是为了使得文件中的打印输出语句仅在本文件运行时执行,而当文件被作为模块导入其它地方时不执行。
- 类的特殊属性和特殊方法
特殊属性
__name 这样前面有两个下划线命名的类的属性属于类的私有属性(Private),它不能直接被类实例访问。如果想访问需要使用类的定义一些方法。
class student(object): def __init__(self, name, classroom): self.__name = name self.__classroom = classroom def getname(self): return self.__name def setname(self, newname): self.__name = newname st1 = student("Ming", 111) print(st1.getname()) st1.setname("Wang") print(st1.getname()) ''' Ming Wang '''
_name 这样前面有一个下划线命名的类属性表示我是类的私有属性但是可以被实例直接访问。
静态方法
在前面的方法中,通常在其中第一个参数传递一个实例对象,以充当调用的一个隐式主体。而有些时候程序需要处理与类而不是实例相关的数据。我们就不期望传递一个 self 实例参数。
# 使用静态方法和类方法之前 class Student: numinstance = 0 def __init__(self): Student.numinstance = Student.numinstance +1 def print(): print("numinstance is ", Student.numinstance) #print = staticmethod(print) s1 = Student() s2 = Student() s3 = Student() # print(s3.print()) #实例调用无 self 参数方法不可行 Student.print() # numinstance is 3
# 使用静态方法和类方法之后 class Student(object): numinstance = 0 def __init__(self): Student.numinstance = Student.numinstance +1 def print(): print("numinstance is ", Student.numinstance) def clsmethod(obj, xx): print(obj.numinstance) print(xx) print = staticmethod(print) # staticmethod 静态方法 clsmethod = classmethod(clsmethod) #classmethod 类方法 s1 = Student() s2 = Student() s3 = Student() s2.print() s3.print() Student.print() ''' numinstance is 3 numinstance is 3 numinstance is 3 ''' Student.clsmethod("ang") # 调用类方法时。会自动把类而不是实例传入类方法的第一个参数, s3.clsmethod("yzq") # 类方法支持类调用也支持实例调用 ''' 3 ang 3 yzq ''' s4 = Student() s4.clsmethod("qzy") ''' 4 qzy '''
实际上,类方法总是接受实例树中最低的那个类。
3、函数装饰器
函数装饰器
装饰器本身就是一个函数,它接受一个函数作为参数,并返回一个新的函数。
import time from functools import wraps def timethis(func): @wraps(func) # 保留被装饰函数的元数据 def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(func.__name__, end-start) return result return wrapper @timethis def countdown(n): while n > 0: n-= 1 countdown(100000) #访问被包装函数的元数据 print(countdown.__name__) print(countdown.__doc__) print(countdown.__annotations__) countdown.__wrapped__(100000) #通过 __wrapped__属性直接访问被包装函数 ''' countdown 0.005457401275634766 countdown None {} '''
带有参数的装饰器
# 带参数的装饰器 from functools import wraps import logging def logged(level, name=None, message=None): def decorate(func): logname = name if name else func.__module__ log = logging.getLogger(logname) logmsg = message if message else func.__name__ @wraps(func) def wrapper(*args, **kwargs): log.log(level, logmsg) return func(*args, **kwargs) return wrapper return decorate # Example use @logged(logging.DEBUG) def add(x, y): print( x + y ) @logged(logging.CRITICAL, 'example') def spam(): print('Spam!') add(2, 3) spam() ''' 5 spam Spam! '''
关于装饰器更多参考cookbook 元编程