有时候面试时会被问到一些细节问题,还是把琐碎的知识点有必要总结整理一下。
9、 函数
9.1 定义函数
在使用函数之前必须先定义(声明)函数,然后才能调用它。使用关键字def 定义函数,语法格式如下:
def 函数名 (参数列表):
<函数语句>
return 返回值
其中,参数列表和返回值不是必需的。如果return 后没有返回值,也没有return 语句,这样的函数会返回None值。使用缩进以语句表示函数体。
【注意】:当函数没有参数时,包含参数的圆括号也必须写上,圆括号后也必须有冒号“:”。
与C语言相比,在python 中声明一个函数不需要声明函数的返回值类型,也不需要声明参数的类型。
调用函数:
函数的参数:
在调用某个函数时,可以向其传递参数,也可以不传递参数,但都不影响函数的使用。
python中,有必须参数、关键字参数、默认参数、不定长参数。
(a)关键字参数
在使用关键字参数时,允许在调用函数时参数的顺序与声明时不一致,因为python解释器能够使用参数名匹配参数值。输出的顺序与形参顺序保持一致。
关键字参数
如果没有传递参数,则使用默认参数。
此外,可选参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
关键字参数
默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!
要注意定义可变参数和关键字参数的语法:
*args是可变参数,args接收的是一个tuple,此种情况类似于不定长参数;
**kw是关键字参数,kw接收的是一个dict。
(b)默认参数
在函数定义时给定一个初始值,在函数调用时可以不传这个参数,采用默认参数的值;下例中的age就是默认参数;
如果在声明一函数时,其参数列表中既包含无默认值的参数,又包含有默认值的参数,那么,在声明函数的参数时,必须先声明无默认值的参数,后声明有默认值的参数。
使用默认参数
(c)不定长参数
在python中,可能需要一个函数能处理比当初声明时更多的参数,这些参数叫“不定长参数”。不定长参数也叫可变参数。
在自定义函数时,如果在参数名前加上一个星号“*”,则表示该参数就是一个可变长参数。
在调用该函数时,如果依次序将所有的其他变量都赋予值之后,剩下的参数将会收集在一个元组中,元组的名称就是前面带星号的参数名。
不定长参数
(d)可选参数
如下面例子中,如果实参个数少于等于形参个数,为了使程序正确运行,需要给“缺失”的实参指定一个默认值:空字符串,并且可以提供也可以不提供这个实参。
也即:可选参数要在形参中指定一个空字符串的默认值。
让实参变成一个可选参数(不提供实参)
让实参变成一个可选参数(提供实参)
按值传递参数与按引用传递参数:
按值传递
按引用传递
Python函数参数支持按值还是按引用传递?
根据具体情况,Python的函数参数既支持按值传递也支持按引用传递。
实际上,解释器会查看对象引用(内存地址)指示的那个值的类型,如果变量指示一个可变的值,就会按引用调用语义。如果所指示的数据的类型是不可变的,则会应用按值调用语义。
列表 字典 集合
总是会按引用传入函数,函数代码组中对变量数据结构的任何改变都会反映到调用代码中。
字符串 整数 元组
总是会按值传入函数,函数中对变量的任何修改是这个函数私有的,不会反映到调用代码中。
【注意】:
在 python 中,字符串strings, 元组tuples, 和 整数numbers 是不可更改的对象,而 列表list, 字典dict,集合set 等则是可以修改的对象。
参考:
https://blog.csdn.net/weixin_41798450/article/details/89306399blog.csdn.net
注意以上两种方式的区分。
python 函数的参数传递:
python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。
python函数中按值传递参数与按引用传递参数的区别:
按值传递:函数内改变,函数外不改变原参数值;
按引用传递:函数内、外都改变原参数的值;
变量的作用域:
分为局部作用域、全局作用域、内置作用域。
局部作用域:定义在函数内部的变量拥有一个局部作用域,表示只能在声明它的函数内部访问。
全局作用域:定义在函数外的变量拥有全局作用域,表示可以在整个程序范围内访问。
内置作用域:python预先定义的作用域。
【注意】:
在函数内外可以有同一个名称的变量而相互不影响。
函数内外使用相互不影响的同名变量
但是在函数中引用全局变量并进行操作,也即在函数中使用函数外的变量,需要在变量名前加上关键字global。
使用关键字global在函数内部使用全局变量的过程
在python中,函数可以返回任意类型的值,包括列表和字典以及比较复杂的数据结构。
函数返回一个字典
使用函数传递列表:
使用函数传递列列表,在这类列表中可能包含名字、数字或更复杂的对象(例如字典)。将列表传递给函数后,函数就可以直接访问其内容。所以,可以使用函数来提高处理列表的效率。
使用函数传递列表
通过在函数中对列表进行修改,可以高效的处理大量的数据。
在函数中修改列表(第一个函数实现复制功能,第二个函数负责输出复制到“relatives”群组中的好友信息)
使用匿名函数:(为了使代码清晰,不复用的函数)
可以使用lambda 来创建匿名函数。所谓匿名,是指不再使用def语句这样标准的形式定义一个函数。也可以将匿名函数赋给一个变量供调用。它是python中一类比较特殊的声明函数方式。其语法格式如下:
lambda params:expr
参数params相当于声明函数时的参数列表找中用逗号分隔的参数,参数“expr”是函数要返回值的表达式,而表达式中不能包含其他语句,也可以返回元组(要用括号),并且还允许在表达式中调用其他函数。
使用lambda创建匿名函数
函数和模块开发:
模块是扩展名为".py"格式的文件。将函数存储在模块的独立文件中,再将模块导入到主程序中。好处:(a)将函数存储在独立的文件中,可以隐藏代码的细节,将重点放在程序的高层逻辑上。(b)在众多不同的程序中可以重用该函数,从而可以与别人共享这些文件而不是整个程序。
导入整个模块文件格式为:
import 模块名
模块名.函数名
只导入指定的函数:
from 模块名 import 函数名1,函数名2,......
使用as指定函数别名:
from 模块名 import函数名 as 别名1
使用as指定模块别名:
import 模块名 as 模块别名
导入所有函数:(但是不推荐这种方式,容易出错)
from 模块名 import *
10. 面向对象(上)
10.1 定义并使用类
把具有相同属性和方法的对象归为一个类。在使用类之前必须先创建这个类。
格式为:
class ClassName:
语句
注意:类的首字母必须大写。
类只有实例化之后才能使用。使用类名加小括号的形式就可以实例化一个类。一个类可以实例化多个实例,实例与实例之间不会互相影响。类实例化之后就可以直接使用了。
10.2 类对象
10.3 类方法
可以使用关键字def 在类的内部定义一个方法。在定义类的方法后,可以让类具有一定的功能。在类外部调用该类的方法就可以完成相应的功能,或改变类的状态。
10.3.1 定义类方法的方式与其他一般函数的定义方式相似,但有以下区别:
(a)方法中的第一个参数必须为self,且不能省略。但是在调用方法时,不用提供self参数。
(b)方法的调用需要实例化类,并以“实例名.方法名(参数列表)”的形式进行调用。
(c)必须整体进行一个单位的缩进,表示这个方法属于这个类。
10.3.2 构造方法
在定义类时,可以定义一个特殊的构造方法,即__init__()方法。
注意:init前后分别是两条下划线“__”。
如果在类中定义了__init__()方法,那么类的实例化操作会自动调用__init__()方法。
也即:使实例自动具有构造函数功能。
【注意】:在python中,以self为前缀的变量都可以供类中所有的方法使用,并且还可以通过类的实例来访问这些变量。通过实例访问的变量称为属性。
使用类对象
10.3.3方法调用
类中的方法既可以调用本类中的方法,也可以调用全局函数来实现相关功能。
调用本类中的方法格式如下:
self.方法名(参数列表)
调用类自身的方法和全局函数
10.3.5 使用私有方法
与大多数语言不同,在python中,函数,方法或属性是私有还是公有,完全取决于它的名字。以两条下划线“__”开始(注意,不是结束),那么该函数、方法或属性就是私有的,其他都是公有的。当在类的内部调用私有成员时,可以用点“.”运算符实现。
在类内部调用私有方法格式:
self.__方法名
【注意】在python中,私有函数,方法和属性具有以下特点:
私有函数不可以从它们的模块外部调用。
私有类方法不可以从它们的类外部调用。
私有属性不可以从它们的类外部访问。
10.3.6析构方法
析构方法为_del()_,前后分别有两条下划线"_”。当使用内置方法del()删除对象时,会调用它本身的析构函数。此外,当一个对象在某个作用域调用完毕后,在跳出其作用域的同时也会调用析构函数一次,这样可以使用析构函数_del()__释放内存空间。
10.3.7 静态方法和类方法
类中方法分为多种,最常见的有实例方法,类方法和静态方法。类方法和静态方法的定义方式与实例方法不同,它们的调用方式也不同。在调用类方法和静态方法时,可以直接由类名进行调用,在调用前无需实例化类。当然,也可以使用类的实例进行调用。
10.4 类属性
既可以在构造方法中定义属性,也可以在类的其他方法中使用定义的属性。
类属性和实例属性:
“self.属性名”的格式定义,在调用时也使用这种格式调用。
【注意】:
对于实例属性来说,两个实例之间并不联系,可以各自独立的修改为不同的值;
对于类属性来说,无论哪个实例修改了它,都会导致所有实例的类属性值发生变化。
使用类属性和实例属性
10.4.3 设置属性的默认值
类中的每个属性都必须有初始值。可以在方法_init__()中指定某个属性的初始值是0或空字符串。如果设置了某个属性的初始值,就不需要在_init__()中提供为属性设置初始值的形参。
设置属性的默认值
10.4.4 修改属性的值
有两种方式修改属性的值:
10.5 继承
这个已有的类称为基类或者父类,而新类称为派生类或者子类。
语法格式为:
class ClassName1(ClassName2):
其中,ClassName1表示子类(派生类)名,ClassName2表示基类(父类)名。
【注意】:
在创建子类时,父类必须包含在当前文件中,且位于子类的前面。
在以上代码中的super( )是一个特殊的函数,功能是将父类和子类关联起来,可以让python调用Car父类的方法__init__(),可以让子类的实例包含父类中的所有属性。父类也称为超类(superclass)。
10.5.2 在子类中定义方法和属性
子类除了可以继承使用父类中的属性和方法外,还可以单独定义自己的新的属性和新的方法。
在子类中定义新的方法和属性
10.5.3 子类可以继续派生新类
可以基于一个子类继续创建一个子类。
10.5.4 私有属性和私有方法
当子类继承了父类之后,虽然子类具有了父类的属性和方法,但是不能继承父类中的私有属性和私有方法(属性和方法名的前缀为两条下划线)。在子类中还可以使用重写的方式来修改父类的方法,以实现与父类不同的行为表现或能力。
10.5.5 多重继承
多重继承是指一个类可以继承多个,在实现多重继承定义时,以逗号“,”分割开要多重继承的父类。具体语法格式:
class 子类名(父类1,父类2,父类3,......)
在多重继承中,继承顺序是个很重要的因素。如果继承的多个父类中有相同的方法名,但在类中使用时未指定父类名,则python解释器将从左到右搜索,即调用先继承的类中的同名方法。
10.6 方法重写
当子类在使用父类中的方法时,如果发现父类中的方法不符合子类的需求,可以对父类中的方法进行重写。在重写时需要先在子类中定义一个这样的方法,与要重写的父类中的方法同名,这样python程序将不会再使用父类中的这个方法,而只使用在子类中定义的和父类中重名的方法(重写方法)。
面向对象(下)
11.1 模块架构
不能随便导入编写好的外部模块,只有被python找到的模块才能导入。
当在程序中导入一个模块时,python解释器首先在当前目录中查找要导入的模块。如果没有找到这个模块,python解释器会从“sys”模块的path变量指定的目录中查找这个要导入的模块。在大多数情况下,python解释器会在运行程序前将当前目录添加到sys.path路径的列表中,所以在导入模块时首先查找的路径是当前目录下的模块。
在调用模块时,如果外部模块文件和测试文件在同一个目录中,运行成功后会在本目录中生成一个名为“_pycache__”的文件夹目录,在这个目录下还有一个名为“模块名.cpython-36.pyc”文件。
“XXX.pyc”格式的文件是一个可以直接运行的文件,这是python将文件“XXX.py”编译成字节码的文件。对于外部文件来说,python总是在第一次调用后将其编译成字节码的形式,以提高程序的启动速度。
如果不想将某个源文件发布,那么可以发布编译后的程序(.pyc文件),这样可以起到一定的保护源文件的作用。也可以调用系统内置模块去编译指定的文件:
编译指定的文件
运行.pyc 文件和.py文件的执行效果是相同的,编译后生成的.pyc 文件并没有改变程序的功能,只是一python字节码的形式存在而已,起到了一个防止源码泄漏的作用。
11.1.3 使用__name__属性
在python程序中,当一个程序第一次导入一个模块时,将会运行主程序。如果想在导入模块时不执行模块中的某一个程序块,可以用“__name__”属性使该程序块仅在该模块自身运行时执行。
因此,可以通过查看“__name__”属性来判断程序的运行状态。
包:(同目录保存同类功能的程序文件)
包其实是个文件夹或目录,但其中必须包含一个名为“__init__.py”的文件。该文件可以是一个空文件,表示这个目录是个包。
在编程过程中,可以将包看作是同一个目录中的模块。
当在python中使用包时,需要首先使用目录名,然后再使用模块名导入所需要的模块。如果需要导入子包,则必须按照包的顺序(目录顺序)使用“.”运算符进行分割,并使用import 语句进行导入。
当使用“from package import item”这种形式的时候,对应的item既可以是包里的子模块(子包),也可以是包里面定义的其他名称,比如函数、类或变量。