python学习笔记
python学习笔记
JJuprising部分来源:廖雪峰的官方网站
函数
函数的参数
使用默认参数解决调用时缺少实参问题,一定必选参数在前,默认参数在后
1 | def power(x, n=2): |
1 | 5) power( |
定义默认参数要牢记一点:默认参数必须指向不变对象!
如:
1 | def add_end(L=[]): |
1 | add_end() |
但是,再次调用add_end()
时,结果就不对了:
1 | add_end() |
要修改上面的例子,我们可以用None
这个不变对象来实现:
1 | def add_end(L=None): |
可变参数
可变参数就是传入的参数个数是可变的
我们把函数的参数改为可变参数:
1 | def calc(*numbers): |
定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*
号。在函数内部,参数numbers
接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:
1 | 1, 2) calc( |
1 | 1, 2, 3] nums = [ |
*nums
表示把nums
这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。
模块
创建模块
- 新建一个.py文件,不要和python自带重名
导入模块
1 | import 模块名称 [as别名] |
例子:
1 | #导入整个 |
导入自定义模块要先将自定义模块所在文件下设置为源文件夹(Sources Root)
第三方模块
1 | pip install 模块名 #在小黑窗在线安装 |
例子:
1 | import schedule |
以主程序运行
1 | if __name__=='__main__': |
例子:
1 | #calc2.py |
包
python程序结构:
graph LR A(Python程序)-->B(包1) A-->C(包2) A-->D(包3) B-->E(模块A) B-->F(模块B) C-->G(模块A) C-->H(模块B)
包与目录的区别
- 包(package)包含_init_.py文件
- 目录(directory)里通常不包含_init_.py文件
导入同目录下的包里的模块:
1 | import package.module_A as ma #别名 |
使用import
导入时只能跟包名和模块名
想要导入变量用from
:
1 | from package.moudle_A import a |
graph LR A(模块与包)-->B(模块) A-->C(包) B-->D(避免函数名/变量名重名) B-->E(自定义模块) B-->F(第三方模块) E-->G(一个.py文件称为一个模块) F-->H(安装:pip install 模块名) F-->I(使用:from ...import 模块名) C-->J(避免模块名重名) C-->K(包含__init__.py文件)
面向对象
类和实例
1 | class Student(object): |
注意:特殊方法“__init__”前后分别有两个下划线!!!
注意到__init__
方法的第一个参数永远是self
,表示创建的实例本身,因此,在__init__
方法内部,就可以把各种属性绑定到self
,因为self
就指向创建的实例本身。
有了__init__
方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__
方法匹配的参数,但self
不需要传,Python解释器自己会把实例变量传进去:
1 | 'Bart Simpson', 59) bart = Student( |
继承和多态
继承
我们已经编写了一个名为Animal
的class,有一个run()
方法可以直接打印:
1 | class Animal(object): |
当我们需要编写Dog
和Cat
类时,就可以直接从Animal
类继承:
1 | class Dog(Animal): |
在括号放父类
多态
在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行:
1 | b = Animal() |
例子:
1 | def run_twice(animal): |
你会发现,新增一个Animal
的子类,不必对run_twice()
做任何修改,实际上,任何依赖Animal
作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。
这就是多态的意思:
对于一个变量,我们只需要知道它是Animal
类型,无需确切地知道它的子类型,就可以放心地调用run()
方法,而具体调用的run()
方法是作用在Animal
、Dog
、Cat
对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal
的子类时,只要确保run()
方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:
对扩展开放:允许新增
Animal
子类;对修改封闭:不需要修改依赖
Animal
类型的run_twice()
等函数。
获取对象信息
使用type()
首先,我们来判断对象类型,使用type()
函数:
基本类型都可以用type()
判断:
1 | type(123) |
使用isinstance()
对于class的继承关系来说,使用type()
就很不方便。我们要判断class的类型,可以使用isinstance()
函数。
我们回顾上次的例子,如果继承关系是:
1 | object -> Animal -> Dog -> Husky |
那么,isinstance()
就可以告诉我们,一个对象是否是某种类型。先创建3种类型的对象:
1 | a = Animal() |
然后,判断:
1 | isinstance(h, Husky) |
使用dir()
如果要获得一个对象的所有属性和方法,可以使用dir()
函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
1 | dir('ABC') |
类似__xxx__
的属性和方法在Python中都是有特殊用途的,比如__len__
方法返回长度。在Python中,如果你调用len()
函数试图获取一个对象的长度,实际上,在len()
函数内部,它自动去调用该对象的__len__()
方法,所以,下面的代码是等价的:
1 | len('ABC') |
我们自己写的类,如果也想用len(myObj)
的话,就自己写一个__len__()
方法:
1 | class MyDog(object): |