函数
函数定义
函数是组织好的,可重复使用的,用来实现单一或者相关联功能的代码块。通过函数的调用,可以提高代码的复用性,极大降低代码冗余(代码重复)。在python中默认提供有很多自带有的函数,例如print()。同时,函数也支持自定义创建,这也叫做用户自定义函数。
''' 函数的参数传递: 1. 参数可以是任何类型的内容,不同数据类型的数据,或者对象,都可以是入参。 2. 在传递参数时,可以指定参数名传递,也可不指定。若不指定则根据参数的顺序对入参进行匹配。 3. 在参数定义时,可以设置参数的默认值。'''# 带参数的函数定义def function_demo(a, b, c): # 此处的a,b,c就是函数的参数,也叫做形参。 print(a) print(b) print(c)# 带默认值参数的函数定义def function_arguments(a, b='默认'): # 此处b参数设置有默认值,在不给b参数传入入参时,使用默认值,传入参数则修改默认值为传入后的值 print(a) print(b)# 调用函数,并进行参数的传递function_demo(1, 2, 3) # 此处的1,2,3表示实参,也是入参。# 指定参数顺序进行传参:通过指定参数进行传参,可以不遵循形参的顺序。function_demo(b=1, a=3, c=2)# 调用带默认值的函数function_arguments(1) # 不传入b参数值function_arguments(1, 2) # 传入b参数值,需与默认值保持同一数据类型,否则会提示警告。
return关键字
虽然在函数中,return关键字一般表示函数的结束,但是在函数的代码逻辑编写时,考虑到各类逻辑场景的处理,所以一个函数中有时候也会包含有多个return关键字,但不管有多少return关键字,函数在实际运行时,都会在调用第一次return关键字时结束函数的运行。
# 复杂一些的return关键字调用# 在整个函数中,根据num的值来进行不同的判断,return对应的结果内容。def return_function(num): if num == 1: return '这是int' elif num == 'str': return '这是str' elif num == 'dict': return '这是dict' else: return '这啥也不是'# 更为复杂的return关键字调用:在一个死循环中定义return,当运行return时,函数结束,死循环也会同步结束。def return_function_plus(): i = 0 while True: print('现在是更为复杂的应用') if i == 4: return 'bingo!' i += 1
函数的调用
函数在被定义好后,是需要通过调用函数来使其生效的。不被调用的函数是没有存在意义的。
''' 函数的调用: 1. 调用方式是通过输入函数名加上括弧号进行调用。 2. 函数在调用时,若需要传入参数,默认情况下,参数值与参数名称是按照函数声明中所定义的顺序匹配起来的。所以传入参数的个数必须与函数保持一致。'''# 无参数版def hello(): print('hello world!')# 有参数版def hello_plus(addr): print('hello {}!'.format(addr))# 函数的调用hello() # 无参数hello_plus('China') # 有参数
函数的进阶调用:函数除去被直接调用以外,也可以被其他函数调用,也就是函数调用函数。一般在处理稍微复杂一些的代码逻辑时,会将部分内容写成函数,再将函数放到另外一个函数中进行调用,达到精简代码与提升维护性的目的。
# 函数调用函数的调用方式与一般调用没有区别。def hello(addr): print('hello {}!'.format(addr))def hello_plus(city): hello(city) # 函数调用函数# 函数的调用hello_plus('CSC')
函数参数传递
如果需要从外部传入数据到函数内进行运算,可以在创建函数时定义参数。参数没有类型限制,可以传递任意类型的值。
''' 函数的参数传递: 1. 参数可以是任何类型的内容,不同数据类型的数据,或者对象,都可以是入参。 2. 在传递参数时,可以指定参数名传递,也可不指定。若不指定则根据参数的顺序对入参进行匹配。'''# 带参数的函数定义def function_demo(a, b, c): # 此处的a,b,c就是函数的参数,也叫做形参。 print(a) print(b) print(c)# 调用函数,并进行参数的传递function_demo(1, 2, 3) # 此处的1,2,3表示实参,也是入参。# 指定参数顺序进行传参:通过指定参数进行传参,可以不遵循形参的顺序。function_demo(b=1, a=3, c=2)
参数传递进阶
在Python中,函数的参数传递其实本质上就是元组或者字典的传递。也就是所谓的*args和**kwargs。
''' *args与**kwargs的参数传递机制: 正常的函数参数,一般会有不定数量。也就是一个到多个参数不等。在python中,可以通过*args和**kwargs来实现参数的传递。 1. *args表示任意多个无名参数,通过元组的形态进行传递,根据函数参数的顺序依次匹配元组中的每一个元素,一般情况下元组长度必须与参数数量保持一致。 此类传递参数的方式,可以在调用函数时非常灵活将所有参数一次性传入,不用再一个个指定。便于函数调用的拓展使用。也就是所谓的不定值不定长传参模式。 2. **kwargs表示关键字参数。通过字典的kv格式进行传入参数,所有传入的参数都会遵循key=value的格式,根据函数的形参名逐一匹配字典的key,将对应value进行传参,一般情况下字典的key必须与形参相同 此类传递参数的方式,可以在调用函数时快速基于形参名将所有的参数一次性赋值。也就是所谓的定值不定长传参。'''# 带参数的函数定义def function_demo(a, b, c): # 此处的a,b,c就是函数的参数,也叫做形参。 print(a) print(b) print(c)# 调用函数的两种传参方式function_demo(*(1, 2, 3)) # *表示调用*args传参,python会自行将元组解包,每一个元素都会一一匹配变成参数。function_demo(**{ 'a': 3, 'b': 2, 'c': 1}) # **表示调用**kwargs传参,python会基于字典的kv对自行匹配函数的每一个参数。
递归函数
递归函数是一个相对比较绕的概念,但其实和编程语法中的循环差不多。递归函数有两个明确定义,一是函数调用自身函数的行为;二是有一个正确的返回条件,也就是递归终止的条件设置。说白了,和循环一样,需要设置一个循环结束的条件。不然容易造成死循环。
因为递归相比较普通的函数以及循环语法而言,会更加占用内存资源。所以一般只会在特定场景下进行使用,解决相对复杂的计算逻辑。
''' 递归函数: 函数调用函数本身的一种形态。函数的定义就是用来调用的,可以被其他函数调用,自然也可以被自己调用。 只是因为函数本身的逻辑会更加复杂,所以递归函数在使用时会造成更多的资源占用。一般只是用于处理特定的算法'''# 非递归函数def multiplication(n): num = n for i in range(1, n): num = num * i return num# 递归函数def multiplication_plus(n): if n == 1: # 正确的结束条件定义 return 1 else: num = n * multiplication_plus(n - 1) # 调用自身函数的行为 print(num) # 如果你算晕了可以加上print(),实时查看num值在每次被调用时的变化 return num# 普通函数的调用print(multiplication(5))# 递归函数的调用print(multiplication_plus(5))
匿名函数
在python语法中,除去标准格式下定义的函数以外,还有使用lambda关键字来创建的匿名函数。通常情况下,主要应用于需要一个函数,但又不想费神去专门定义一个函数时,作为一次性使用的一句话函数。
''' 1. lambda匿名函数不需要通过def来进行定义,本身只是一个表达式,比常规函数要简单很多 2. lambda的主体基本只有一行代码,而不是一个代码块,所以能实现的逻辑非常有限,只用于简单的代码逻辑 3. lambda函数虽然看起来简单许多,但是除了调用与一般函数有区别外,其他基本一致。比如说函数内与函数外相互独立,数据不互通等。'''# 匿名函数示例x = lambda a: a + 10 # 定义一个匿名函数,要求传入参数a,函数体为计算a+10,将此函数赋值到xprint(x(10)) # 调用匿名函数y = lambda a, b: a + b # 定义一个多参数的匿名函数,将此函数赋值到yprint(y(2, 3))
Global关键字的使用
定义函数时,一般函数的操作是无法修改函数外的变量值。如果想通过调用函数来修改函数外变量的值,则需要在函数中将该变量声明为global变量,也就是全局变量。在函数中声明之后,则可以改变它的值。
''' 函数外的变量不会因为函数运行对其进行的操作而修改变量本身的值。如果想要实现修改的效果,则需将变量声明为全局变量才可。'''a = 8def plus(): global a # 定义a为全局变量之后,若对a进行了值的修改,则会影响到函数以外的a变量本身的值。 a = 10 return a + 5print(a) # 函数调用前,a = 8print(plus()) # 调用含有global的函数print(a) # 函数调用后,a = 10
类
类与对象的定义和使用
用来描述具有相同的属性和方法的对象的集合叫做类。它定义了该集合中每一个对象所共有的属性和方法。对象则是类的实例。
''' 类的定义: 所有类都是通过class关键字进行创建。类是由类方法、类属性等组成的集合 对象是类的实例。类无法被直接调用,如果要操作类,则需通过实例化对象后,基于对象来进行操作。'''# 创建一个demo类class Demo: # 类属性 a = '类属性' # 类方法 def function_demo(self): print('类方法')# 实例化Demo类,生成实例化类对象demo = Demo()# 通过实例化的类对象调用类方法和类属性。实现类的使用demo.function_demo()print(demo.a)
类属性
也叫做类变量,是在类中方法之外定义的变量。既可以通过类名访问,也可以通过对象名访问,被类的所有对象共享。
''' 类属性: 定义在类中且在方法外的变量,叫做类属性。 类属性的访问分为类内部访问与类外部访问两种方式: 1. 类内部,如果在类方法中需要调用类属性,需通过self关键字访问 2. 类外部,如果需要访问类属性则通过类名.类属性和对象名.对象属性'''# 创建一个demo类class Demo: # 类属性 a = '类属性' # 类方法 def function_demo(self): print(self.a) # 内部调用类属性,需通过self关键字# 类外部调用类属性print(Demo.a) # 通过类名直接调用# 通过实例化类对象调用dd = Demo()print(dd.a)
类属性进阶操作
类属性被所有类对象的实例对象所共有,在实际代码中,分为基本的类属性与实例化后的实例属性两种类型。
''' 实例化后调用的是实例属性,该属性的修改与删除只会影响对应的实例化对象,对原本的类不会产生任何影响。 类属性在修改后,也不会影响实例化对象的属性赋值。所以类属性与实例属性是相互独立的存在。'''# 创建一个demo类class Demo: # 类属性 a = '类属性'dd = Demo()print(dd.a)# 修改类属性Demo.a = 'before create'print(Demo.a)print(dd.a)# 通过实例化类对象对实例属性进行修改与删除dd.a = 'new attribute' # 修改实例化对象的属性print(dd.a)del dd.a # 删除实例化对象的属性print(dd.a)
类方法
类的方法主要分为三种,分别是类方法、静态方法、实例方法。定义类方法与定义函数的语法一致,都是通过def关键字声明,只是类方法是在类中定义,而函数不用在类中定义。
''' 类方法的定义: 1. 类方法:通过self进行调用 2. 静态方法:通过类名调用 因为静态方法中不需要传入self参数,所以静态方法的调用是直接通过类名调用,也因为缺少self,所以静态方法无法获取类其他属性与方法。 3. 实例方法:实例化后调用'''class demo: a = 'sd' # 类方法 def function_in_class(self): print('这是类方法') # 静态方法:需声明@staticmethod装饰器 @staticmethod def function_static(): print('这是类静态方法')# 类静态方法的调用,不需要实例化类,直接通过类名调用即可,静态方法无法直接获取类中其他的方法和变量。是相对独立的存在。demo.function_static()# 类方法的调用,需要通过实例化的形式调用,否则会提示缺少self参数demo().function_in_class()
构造函数
构造函数是一种特殊的函数,在创建类对象时,会默认调用。如果没有定义构造函数,则会自动创建一个不执行任何操作的默认构造函数。如果我们想要在实例化类对象时,为其做一些实例内容的预设,也可以自定义一个构造函数,在调用时运行。
''' 构造函数,默认不执行任何操作,也不需要专门定义。在实例化时python会自行帮我们调用一个不执行任何操作的默认构造函数。 如果对于实例化对象有初始化需求,需要在实例化时进行一部分的默认设置,则可以自己重新定义构造函数的相关内容。 构造函数在类中有固定的命名:__init__()。 如果设置有构造函数,实例化类对象时必须遵循构造函数的参数要求。'''class person: # 构造函数:进行默认设定 def __init__(self, name, age): self.name = name self.age = age # 调用构造函数中赋值的内容进行函数逻辑处理 def info(self): print('Person的基本信息如下:姓名:{0},年龄:{1}'.format(self.name, self.age))# 实例化person类对象:因为更改了构造函数,所以在实例化时需要传入name和age两个参数hcc = person('hcc', '28')hcc.info()# 因为构造函数的存在,不同的实例对象具备有不同的实例成员xzl = person('xzl', '18')xzl.info()
self关键字
''' self的用法定义: 在Python类中规定,普通类方法的第一个参数是实例对象本身,并且定义为关键字self。表示当前类 的对象。可以通过self来调用当前类中的属性和方法。 self只用于实例化对象调用普通方法。对于静态方法这种不需要实例化即可调用的函数。不需要传递self。 在类中,想要调用类本身存有的属性或者方法,也可直接通过self来进行调用。'''class demo: a = 'sd' # 类方法 def function_demo(self): print('这是类方法') # 静态方法 @staticmethod def function_static(): print('这是类静态方法') # 通过self调用类本身的属性和方法 def function_self(self): print(self.a) # 在类方法中调用类属性,通过self调用 self.function_demo() # 在类方法中通过self调用普通类方法 self.function_static() # 在类方法中通过self调用静态类方法# 实例化类对象,其实就是创建了一个self对象。所以实例化后调用函数不用再额外传入selfd = demo()d.function_self()
面向对象
封装
在设计类的时候,刻意将一些属性或方法隐藏在类的内容,这样就无法直接以“类对象.属性名”(或者“类对象.方法名(参数)”)的形式被调用这些属性和方法。而只能操作未被隐藏的那些属性和方法。这个就是封装。
通过封装机制,可以很好地保证类内部数据结构的完整性,因为类的使用者无法直接看到类中的数据结构。很好地避免了外部对内部数据的影响。也提升了程序的可维护性和复用性。
''' 封装的私有属性与方法: 在python语言中,没有像是java那样的public和private装饰符来定义属性与方法的权限。 如果想要在类中定义到专属于类内部的私有属性和方法,则可以通过在属性或方法的名字前添加__来表示 这是python对于私有化的标准命名写法。 私有属性无法在类外被调用,也无法被实例化对象调用 私有属性与方法的使用,只限于在类本身之中,这是封装上,对于类内部不愿被对外的内容的一种写法。 如果想要获取这些私有内容,则需要通过公有方法来调用,或者通过return将私有属性返回。'''class demo: a = '公有属性' __b = '私有属性' def public_function(self): print('这是公有方法') def __private_function(self): print('这是私有方法') # 定义公有方法,调用私有属性与方法。达到外部使用的目的。 def function_demo(self): self.__private_function() return self.__bd = demo()print(d.a)# print(d.__b) # 报错提示demo类没有这个属性d.public_function()# d.__private_function() # 报错提示demo类没有这个方法print(d.function_demo()) # 通过公有方法调用私有方法,return私有属性
继承
在面向对象思维逻辑下,有一个重要的环节就是继承。继承的定义,可以让我们更快实现一整套规范化的类的内容定义。
通过继承,允许我们定义继承另一个类的所有方法和属性的类。继承类叫做子类。被继承类叫做父类,也叫做基类。
''' 继承: 继承语法: class Student(Person): 创建一个Student类,继承于Person类 继承允许我们定义继承另一个类的所有方法和属性的类。在python中,所有的类(包括自定义类)都 默认继承于object类。 任何类都可以是父类,任何类都可以被其他类继承。但是我们在进行继承操作时,一般会选择彼此具备相关特性的类。'''# 创建父类class Person: # 定义构造函数 def __init__(self, name, sexual): self.name = name self.sexual = sexual # 定义父类方法 def speak(self): print('这是一个名字叫做{}的{}人在说话'.format(self.name, self.sexual))# 创建子类,继承于Person类class Student(Person): # 不定义子类任何的内容 pass# 实例化子类Student对象,可以直接调用Student类所继承的所有内容,包括构造函数与speak函数stu = Student('名字', '男')stu.speak() # 正常执行父类的speak函数
多态
多态指的是一类事物有多种形态,一个类有多个子类,不同子类对象调用相同的方法,产生不同的执行结果,从而让代码更加灵活。
多子类的多态实现,可以通过方法重写的形态来实现。
方法重写与方法重载
在完成继承后,子类包含有父类的所有的属性和方法。但代码的实际执行时,可能因为子类自身的独特性,会对父类原有的方法进行修改。修改的内容包括方法的执行代码块修改与方法的参数数量变化。这就会关系到方法的重写与重载。但是Python中默认是不支持重载写法的。所以重载在这里只做概念的描述。
''' 方法重写与重载: 1. 方法重写,子类对父类中继承而来的方法进行代码块的重新编写,虽然 方法名称相同且参数相同,但方法本身在子类和父类中已经截然不同 2. 方法重载,子类因为逻辑需要,可能在类中包含有非常多个同名函数,但 这些函数的参数以及返回类型都不同,在实例化后通过传入不同的参数实 现不同的方法调用,python默认不支持方法重载的语法结构,所以这里只 讲解概念'''# 创建父类class Person: # 定义构造函数 def __init__(self, name, sexual): self.name = name self.sexual = sexual # 定义父类方法 def speak(self): print('这是一个名字叫做{}的{}人在说话'.format(self.name, self.sexual))# 创建子类,继承于Person类class Student(Person): # 方法的重写:虽然还是speak方法,但是方法中的代码块与父类已经不同,调用时父类与子类的方法运行结果截然不同 def speak(self): print('这是一个学生,名字叫做{},性别为{},这个孩子正在念书'.format(self.name, self.sexual))person = Person('父类', '男')person.speak() # 执行父类原有的speak方法stu = Student('学生', '女')stu.speak() # 执行子类重写的speak方法
多继承
除去多个子类继承自一个父类外,也可以一个子类继承多个父类,实现多继承的效果。让一个子类同时具备有多个父类的属性与方法。
''' 多继承,允许一个子类继承于多个父类,继承多个父类中的所有方法和属性。 需要额外注意的是,如果继承多个父类,调用父类中的同名方法和属性时,会基于继承顺序进行就近匹配 例如: class A(B,C),在同时继承B和C类后,若调用同名方法或属性时,会先在B类中查找,如果找到则直接调用,若没找到则继续在C类中查找 class A(C,B),同样是继承B和C类,但C类在前,所以会现在C类中查找,如果没找到再在B类中查找'''# 创建父类1class Person: # 定义构造函数 def __init__(self, name, sexual): self.name = name self.sexual = sexual # 定义父类方法 def speak(self): print('这是一个名字叫做{}的{}人在说话'.format(self.name, self.sexual))# 创建父类2class Child: # 定义构造函数 def __init__(self): print('这是Child构造函数') # 定义父类方法 def speak(self): print('我是child,我会咿咿呀呀~')# 创建子类1,继承于Person类和Child类class Student(Person, Child): pass# 创建子类2,继承于Child类和Person类class Player(Child, Person): passstu = Student('stu', 'female') # 继承于Person类和Child类,所以优先调用Person类中的构造方法与类方法stu.speak()pla = Player() # 继承于Child类和Person类,所以优先调用Child类中的构造方法和类方法pla.speak()
模块
Python模块,是一个Python文件,以.py结尾,包含了python对象定义和python语句。模块可以让我们能够更加有逻辑地租住我们的python代码块。
python官方提供有非常多模块,可以直接导入使用。
''' Python模块在实际使用时,是通过import语句来引入的。 例如: import time 要引入time模块所有内容 有时候我们并不需要引入一个完整的模块,可能只需要引入模块中的某一个函数,则可以指定内容引入 若要引入多个函数,则在引入时通过,进行分割 from time import sleep, struct_time'''# 只有引入对应的模块后,才可以调用该模块实现需要执行的逻辑。import time# 调用time库的sleep函数。time.sleep(5)# 从time模块只引入sleep函数和struct_timefrom time import sleep, struct_time# 调用sleep函数sleep(4)
Python自定义模块的导入,需要自行先完成一个py文件的编写,内容可以是类相关,也可以直接编写函数。再通过import导入使用。
# 自定义Python模块的demo.py文件内容class ModuleName: def module_function(self): print('this is module_function')# 导入自定义模块的main.py文件内容''' Python自定义模块的调用: Python也支持自定义模块的导入和使用。通过一个py文件将自定义模块的内容编写完成,可以是 类,也可以是函数的形态。在其他py文件中如果需要调用该自定义模块,则通过导入的形态来实现。'''# 导入自定义的ModuleName模块。demo是py文件的名称,ModuleName是类名from demo import ModuleName# 生成实例化对象mn,通过mn调用ModuleName的方法mn = ModuleName()mn.module_function()
Python有着非常丰富的第三方库,如果在编写程序时需要调用第三方库来实现更多的逻辑,可以通过安装第三方库来解决这个问题。
安装的方式是在cmd中通过输入pip指令来进行安装。
安装指令详解:
基本的安装指令:pip install 模块名
因为pip是python自带的安装指令,默认官方下载源的速度相对比较慢,所以可以在安装时通过指定国内源的方式来提升下载速度
完成安装指令,以selenium安装为例:pip install selenium -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
各类模块的安装,直接输入名称通过pip install 安装就好,所以提前确认模块的名称,避免安装时名称输入错误
异常处理
在编码过程中,总是避免不了会出现各种各样的运行时错误。而python在遇到错误时,会终止程序的运行。虽然会明确告诉我们错误发送在什么位置,以及错误产生的原因。以便于我们进行修改与调试。但很多时候我们并不希望因为错误而导致程序的中断运行,因此我们就需要掌握对异常的处理。
程序遇到的异常一般分为ERROR(错误)和Exception(异常)两类。
不论是错误还是异常,如果不做任何处理,程序会直接终止运行。所以我们在编写代码时,对于可能容易出错的地方需要提前进行容错处理。并结合异常对程序进行对应的操作。和其他编程语言一样,python中,处理异常与错误的关键字是try:...except:...语句块。
''' try/Except语句块基本语法: try: 容易出现报错的代码 except: 出现错误后的处理 其实try/except语句块的处理逻辑和if控制流很像,都是根据实际情况进行判断,如果try中出现了报错,则进入except进行处理,如果try中 没有出现报错,则except不会被执行。'''# try/except示例try: a = 1 / 0 # 因为除数不能为0,所以此处会产生报错except: print('代码报错啦。') # 因为try/except语句块提前做了错误处理,所以当出现错误时进入到except部分。程序继续运行。不会终止print('这是报错之后的执行')
Exception对象
在Python中,会使用异常对象来表示异常情况,Exception对象是基础类,根据不同的异常,会有各种子类。在进行try...except时,可以指定Exception对象及其子类进行直接的捕获。
''' 在except中,可以指定捕获的异常类型,通过调用Exception类及其子类来实现。 在指定异常类型进行捕获时要注意,如果是指定的特定的异常,若代码报错不是次异常, 则依旧会报错。如果想捕获所有的异常类型,建议使用基本类Exception'''# Exception捕获try: a = 1 / 0 # 此处报异常ZeroDivisionErrorexcept ZeroDivisionError as e: # as关键字,将ZeroDivisionError重新命名为e,可以简化复杂的名称 print(e) # 通过except可以直接捕获该异常# try:# a = [0, 1]# print(a[5]) # 此处会报异常下标越界# except ZeroDivisionError as e: # 定义除数为零异常进行捕获,因为实际报错与捕获异常不同,所以依旧会报错。# print(e)try: a = [0, 1] print(a[5]) # 此处会报异常下标越界except Exception as e: # 定义异常基础类进行捕获,因为所有的Exception和Error都属于Exception子类,所以能捕获成功所有的异常类型。 print(e)
else语法应用
在对可能发生异常的代码块中进行try...except...进行异常处理时,根据代码逻辑运行,可能会需要关联到一些判断操作。如果发生异常,则进入except之中,如果没有发生异常,则可以进入到else之中。与if...elif...else...语法结构的逻辑类似。
''' try...except...else语法结构,其实和if...else类似。 执行try语句块,如果没有出现异常,则进入到else中,如果出现异常并被捕获,则进入到except中。'''# else语法应用try: a = 1/0 print('这是try')except IndexError as e: print(e)else: print('这是else')
finally语法应用
在捕获异常的时候,即使发生了无法捕获的异常,但依旧还是想要执行一些语句,这个时候就可以通过使用finally语句来实现。
''' finally语句,不会考虑异常是否捕获成功,都一定会执行。 一般用于在最后进行资源释放之类的相关操作,确保对应资源即便在代码出现异常后也能正常关闭。'''# else语法应用try: a = 1 / 0 print('这是try')except IndexError as e: # 异常与实际产生的异常不同,所以异常捕获失败。 print(e)else: print('这是else')finally: # finally语句只能写在try...except...else的最后。 print('这是finally,无论前面怎么样,我都会执行的。')
raise关键字用法
当程序出现异常时,系统会抛出异常,除此之外,我们也可以自行定义让程序抛出异常。想要实现自定义抛出异常,则需使用到raise关键字来完成。
常态化的异常产生,是因为程序本身的逻辑出现问题。但更多的时候程序是否需要引发异常,是根据程序的业务逻辑来决定的。比如说代码需要接收一个1-10的整数,但是却传入了10以上的整数,虽然代码可以正常运行,但不满足程序本身的业务罗了。这时就可以手动抛出异常。
需要注意的是,抛出异常不等于处理异常,抛出异常,但异常依旧存在,而处理异常则是对异常进行对应的处理行为。
''' raise的三种用法: 1. 直接定义raise关键字,不带任何参数。虽然在pycharm中显示有红色,但实际可以运行。 raise默认抛出"No active exception to reraise"的RuntimeError 2. 在raise关键字后加上Exception相关对象,可以指定抛出的异常类型。也可在Exception 相关对象中,对异常的信息进行自定义描述 3. 可以自行定义一个自定义异常类,继承于Exception基础类。再通过raise来抛出自定义异常,整体 实现与前面两者应用相同'''# raise关键字应用1:单独调用raise,不带任何参数try: i = int(input('请输入小于10的正整数:')) a = 100 / i if i >= 10: raise # 虽然提示报错信息,但程序可以正常运行,默认抛出RuntimeErrorexcept RuntimeError as e: print(e)# raise关键字应用2:raise Exception对象,添加异常相关信息try: i = int(input('请输入小于10的正整数:')) a = 100 / i if i >= 10: raise ValueError('这是valueError') # 自主抛出ValueError,并在括号中定义异常的具体说明信息except Exception as e: print('这是异常:{}'.format(e)) # 当输入10及以上的正整数时,会触发try中的raise,抛出ValueError。在此处会将异常信息打印 # raise # 因为在try中已经使用raise关键字抛出异常,在except中也可以通过raise将异常继续抛出,若抛出,则异常依旧存在,不会被处理。# raise关键字应用3:通过自定义异常类,然后raise抛出# 自定义异常类继承于Exception类class DemoException(Exception): passtry: i = int(input('请输入小于10的正整数:')) a = 100 / i if i >= 10: raise DemoException('自定义异常的抛出') # 通过自定义异常来让程序抛出。except Exception as e: print(e) # 显示为DemoException异常相关信息
Traceback模块
python中的异常都是通过traceback模块来跟踪异常返回信息,并输出在控制台中。通过try...except...捕获异常后,通过print只能输出异常的名字,再通过对代码逻辑的理解或者自己的经验来定位与处理这些异常。这无疑对我们的问题排查增加了很多不必要的麻烦。所以可以直接通过调用traceback模块来实现异常信息的追踪与输出,这样可以直观看到更多异常的相关情况,方便我们的调试。
''' traceback模块: 将异常信息完整输出,便于我们问题的定位与处理'''import traceback# 调用traceback模块实现异常的输出try: i = int(input('请输入小于10的正整数:')) a = 100 / i if i >= 10: raise # 虽然提示报错信息,但程序可以正常运行,默认抛出RuntimeErrorexcept RuntimeError as e: traceback.print_exc() # print_exc()函数可以将异常的相关信息显示在控制台
文件操作
python中通过open()函数来打开文件,创建一个file对象,用来对其进行读写操作。
''' open函数,用来操作文件的常用函数,可以将指定文件打开。创建一个file对象 open()函数有三个参数,分别是文件名称(包含路径),打开模式(读或者写),编码模式(便于文件解析) 文件名称:填写时需要包含路径在内,读取路径起始是基于当前执行的py文件开始的。 打开模式: 1. r 只读模式 2. w 写入模式,如果文件存在,则会删除文件原有的内容重新写入,若不存在则会新建一个文件 3. b 二进制模式,一般关联读或写,rb/wb,用于对非文本文件如图片等的操作 4. a 追加模式,如果文件存在,新内容会被写入已有内容有之后,若不存在则会新建一个文件 编码模式:用于确保打开文件后读取的内容,能够正常被读取及显示。 文件操作行为: 读: 1. read():读取整个文件所有内容 2. readline():每次读取一行 3. readlines():按行读取文件,保存为list 写: write():写入文件内容 writelines():将一个字符串list写入文件 在文件操作后,要记得调用file.close()来关闭该文件。确保资源被正常关闭。'''# 读取文件内容file = open('./file_dir/demo.txt', 'r', encoding='utf8')print(file.read()) # 读取整个文件的所有内容# print(file.readline()) # 读取一行文件内容# content = file.readlines() # 按照行为单位将文件内容全部读取并保存为list# print(content) # 因为读取出来后是list,所以通过循环把每一行的内容全部输出# for line in content:# print(line)file.close()# 对文件进行写入操作:文件写入,会将源文件的内容清空,再写入file1 = open('./file_dir/demo1.txt', 'w', encoding='utf8')file1.write('我是写入的内容')file1.close()# 对文件进行追加内容操作:对已有内容的文本文件进行新内容的追加,追加内容在文件末尾# file2 = open('./file_dir/demo.txt', 'a', encoding='utf8')# file2.write('\n我是追加的内容')# file2.close()# 新建一个图片文件,将已有的图片写入到新建的图片文件中image = open(r'D:\desktop_pic.jpg', 'rb') # 已有的图片内容file_3 = open('./file_dir/image.jpg', 'wb') # 新建一个文件对象,等待图片内容写入file_3.write(image.read())file_3.close()image.close()
with open语法应用
为了确保被操作文件资源不会因为代码操作而出现任何问题,所以在文件操作结束时需要调用close函数来关闭该文件,但实际情况下经常容易忘记close操作。所以python提供有with open语法,在此语法下文件执行完会自行调用close操作。
''' with open语法结构: with open() as file: 代码块 因为文件操作到最后一定需要有关闭,不然容易造成文件锁或者损坏文件等问题的出现。 为了避免忘记关闭操作,所以在python中提供有with open语法,会在最后自动关闭 文件,不用再调用close函数手动关闭了。'''# with open语法:打开指定文件,并通过as为打开的文件对象命名with open('./file_dir/demo.txt', 'r', encoding='utf8') as f: print(f.read()) # 操作文件print('这是with open外的代码') # 运行到with open外时,打开的文件自动close# 使用with语法打开多个文件。每个open都用,进行分割。with open(r'D:\desktop_pic.jpg', 'rb') as image, \ open('./file_dir/image.jpg', 'wb') as f: f.write(image.read())
常用内置函数
反射机制
反射机制是一种基于字符串的驱动形态,通过字符串去模块查找函数、属性等。通过反射机制可以使得程序具有在运行时动态修改自己的结构和行为的能力,极大地方便了代码逻辑的设计。
''' Python反射机制常用方法包括四个: 1. getattr()获取模块、类、对象中的属性或方法,便于直接调用或取值 2. setattr()对对象的属性设置新值 3. hasattr()判断模块、类、对象中是否含有该属性或方法,返回True或False 4. delattr()删除对象中的属性,通过类名来进行删除'''# 反射示例# 定义一个基本类class Demo: a = '这是属性' def function_demo(self): print('这是方法')d = Demo()# getattr()示例:获取方法时需要在末尾添加一组括弧号print(getattr(d, 'a')) # 获取d对象中的a属性getattr(d, 'function_demo')() # 获取d对象中的function_demo方法。# setattr()示例:可以对对象进行新属性的设置以及对已有属性值的修改setattr(d, 'b', '这是setattr') # 在d对象中新增b属性print(getattr(d, 'b')) # 输出d对象中的b属性setattr(d, 'b', '这是修改后的setattr') # 对d对象中已有的b属性进行值的修改print(d.b) # 输出d对象中的b属性(修改后)# hasattr()示例:可以判断对象中是否有某个属性,返回结果为True或者Falseprint(hasattr(d, 'a')) # 判断d对象是否有a属性,此处返回Trueprint(hasattr(d, 'function_demo')) # 判断d对象是否有function_demo属性,虽然function_demo是函数,但依旧返回Trueprint(hasattr(d, 'c')) # 判断d对象是会否有c属性,此处返回False# delattr()示例:可以删除目标对象中的指定属性delattr(d, 'b') # 删除d对象中的b属性delattr(d, 'c') # 删除d对象中不存在的c属性则会报错delattr(d, 'function_demo') # 删除d对象中的function_demo函数,报错
生成器与迭代器
可迭代对象(Iterable)
迭代,也就是所谓循环的原理,在python中是最强大的功能之一,用于遍历访问序列元素的一种方式。字符串、列表、元组、字典、集合、打开的文件这些都是可迭代对象。只有可迭代对象才可以基于常规的for...in...循环语句进行迭代。序列数据类型、生成器(包括生成器和带yield的生成器函数)都属于可迭代对象。
迭代器对象(Iterator)
在python中,可以被next()函数调用并不断返回下一个值的对象称为迭代器,迭代器表示的是一个数据流,可以只在需要时才去调用next来计算出一个值。迭代器同一时刻在内存中只有一个值,因为可以存放无限大的数据流。迭代器从第一个元素开始访问,直到所有的元素访问完毕为止。迭代器只能往前不能后退。
''' 迭代器:取值时程序会每次取一个值,取值之后会停留在第一次结束,等到第二次 调用迭代器时,会从第二次重新运行,所以迭代器与普通程序运行不同,不会 在一次调用时就结束。'''# 创建迭代器对象li = ['1', 2, '3', 4]print(type(li)) # 输出listit = iter(li) # 将list转为迭代器对象print(type(it)) # 输出list_iterator# 迭代器取值的两种方式:通过next()函数和循环的方式取值print(next(it)) # 取第一个值print('*' * 20) # 分隔符for i in it: # 从第二个值开始循环 print(i)
生成器对象(generator)
在python中,使用yield的函数被称为生成器,和普通的函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代。所以生成器其实也属于迭代器。
yield函数参照之前的代码理解即可。
关注作者微信公众号 —《蜀道衫》
了解更多软件测试开发知识以及最新面试宝典