python学习笔记

部分来源:廖雪峰的官方网站

函数

函数的参数

使用默认参数解决调用时缺少实参问题,一定必选参数在前,默认参数在后

1
2
3
4
5
6
def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
1
2
>>> power(5)
25

定义默认参数要牢记一点:默认参数必须指向不变对象!

如:

1
2
3
def add_end(L=[]):
L.append('END')#向list尾插入
return L
1
2
>>> add_end()
['END']

但是,再次调用add_end()时,结果就不对了:

1
2
3
4
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']

要修改上面的例子,我们可以用None这个不变对象来实现:

1
2
3
4
5
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L

可变参数

可变参数就是传入的参数个数是可变的

我们把函数的参数改为可变参数:

1
2
3
4
5
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum

定义可变参数和定义一个list或tuple参数相比,仅仅在参数前面加了一个*号。在函数内部,参数numbers接收到的是一个tuple,因此,函数代码完全不变。但是,调用该函数时,可以传入任意个参数,包括0个参数:

1
2
3
4
>>> calc(1, 2)
5
>>> calc()
0
1
2
3
>>> nums = [1, 2, 3]
>>> calc(*nums)
14

*nums表示把nums这个list的所有元素作为可变参数传进去。这种写法相当有用,而且很常见。

模块

创建模块

  • 新建一个.py文件,不要和python自带重名

导入模块

1
2
import 模块名称 [as别名]
from 模块名称 import 函数/变量/类

例子:

1
2
3
4
5
6
7
#导入整个
import math
print(math.pi)

#导入部分
from math import pi
print(pi)

导入自定义模块要先将自定义模块所在文件下设置为源文件夹(Sources Root)

第三方模块

1
2
3
pip install 模块名 #在小黑窗在线安装

import 模块名 #在python文件使用

例子:

1
2
3
4
5
6
7
8
9
10
import schedule
import time

def job():
print("hhh")

schedule.every(3).seconds.do(job)#每三秒执行一次
while True:
schedule.run_pending()#启动
time.sleep(1)#停一秒

以主程序运行

1
2
if __name__=='__main__':
pass

例子:

1
2
3
4
5
6
7
#calc2.py

def add(a,b)
return a+b

if __name__=='__main__'
printf(add(10,20))#只有当运行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
2
import package.module_A as ma #别名
print(ma.a)

使用import导入时只能跟包名和模块名

想要导入变量用from:

1
from package.moudle_A import a

image-20220923230121039

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
2
3
4
5
6
7
8
class Student(object):

def __init__(self, name, score):
self.name = name
self.score = score

def print_score(self):
print('%s: %s' % (self.name, self.score))

注意:特殊方法“__init__”前后分别有两个下划线!!!

注意到__init__方法的第一个参数永远是self,表示创建的实例本身,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

有了__init__方法,在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:

1
2
3
4
5
>>> bart = Student('Bart Simpson', 59)
>>> bart.name
'Bart Simpson'
>>> bart.score
59

继承和多态

继承

我们已经编写了一个名为Animal的class,有一个run()方法可以直接打印:

1
2
3
class Animal(object):
def run(self):
print('Animal is running...')

当我们需要编写DogCat类时,就可以直接从Animal类继承:

1
2
3
4
5
class Dog(Animal):
pass

class Cat(Animal):
pass

在括号放父类

多态

在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行:

1
2
3
>>> b = Animal()
>>> isinstance(b, Dog)
False

例子:

1
2
3
4
5
6
7
8
9
10
11
def run_twice(animal):
animal.run()
animal.run()

>>> run_twice(Animal())
Animal is running...
Animal is running...

>>> run_twice(Dog())
Dog is running...
Dog is running...

你会发现,新增一个Animal的子类,不必对run_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。

这就是多态的意思:

对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在AnimalDogCat对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

  • 对扩展开放:允许新增Animal子类;

  • 对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。

获取对象信息

使用type()

首先,我们来判断对象类型,使用type()函数:

基本类型都可以用type()判断:

1
2
3
4
5
6
>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>

使用isinstance()

对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数。

我们回顾上次的例子,如果继承关系是:

1
object -> Animal -> Dog -> Husky

那么,isinstance()就可以告诉我们,一个对象是否是某种类型。先创建3种类型的对象:

1
2
>>> a = Animal()
>>> h = Husky()

然后,判断:

1
2
3
4
>>> isinstance(h, Husky)
True
>>> isinstance(h, Animal)
True

使用dir()

如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:

1
2
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:

1
2
3
4
>>> len('ABC')
3
>>> 'ABC'.__len__()
3

我们自己写的类,如果也想用len(myObj)的话,就自己写一个__len__()方法:

1
2
3
4
5
6
7
>>> class MyDog(object):
... def __len__(self):
... return 100
...
>>> dog = MyDog()
>>> len(dog)
100