面向过程编程

新的一年,当然是先祝自己happy birthday啦,再进行py的学习,欧克

计算机指令和程序:

计算机硬件系统是由运算器,控制器,存储器,输入设备,输出设备组成,其中运算器和控制器又被称为中央处理器,传统计算机采用冯诺依曼结构,其两大特点是:1. 中央处理器和存储器分离 2.采用二进制

注释

学习一门语言最开始应该知道的就是如何进行注释,因为有了注释,别人甚至是自己才知道这一段代码表示什么意思,用# 开头,后面跟着是注释的内容,#%%可以划分代码块

1
2
3
# 初识python
msg='hello!world'
print(msg)

数字

包括整数(integer)与浮点数(float),整数可以表示任意大小的整数,与c里面的短整型和长整型不一样,在python里面不加区分,3.2e-4值得是3.2*10^-4

数据类型

:整型,浮点型,字符串型,布尔型,复数型,其中整型里面的不同进制这里提一下记法:0b 二进制 ; 0o 八进制; 0x 十六进制;当然还有传统的十进制. 布尔型就是True和False两种类型。复数型就是3+4j,虚数单位换成了j。使用type()函数可以检查数据的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
a=321
b=25
print(a,a+b)
print(a-b)
print(a/b)
print(a//b)
print(a*b)
print(a**b)
a=123
b=2.5
c='ima'
d=True
e=2+3j
print(type(a),a)
print(type(b))
print(type(c))
print(type(d))
print(type(e))
1
2
3
4
5
6
7
# int() 将数据变为整数
# input() 从键盘中获得输入的数据
# %d %f 实现整数,浮点数 占位符
a=int(input('a='))
b=int(input('b='))
print('%d + %d = %d '%(a, b, a+b ))
print('%d - %d = %d '%(a, b, a-b ))
1
2
3
4
# 华氏变摄氏
f=float(input('f='))
c=(f-32)/1.8
print('%fF变成摄氏温度是%dC'%(f,c))
1
2
3
4
5
# 输入圆的半径输出周长和面积
import math as m
r=eval(input('r=')) #input 输入的数据类型是字符,所以需要把它强制变为float或者int,或者用eval函数把他的''去掉,变成一个数据,而非字符串
print(type(r))
print('圆的周长是{:.2f} 圆的面积是{:.2f}'.format(2*m.pi*r,m.pi*r**2))
1
2
3
4
5
# 输入年份判断是不是闰年,是输出TRUE,不是输出False
# 是4的倍数但不是100的倍数是普通闰年,是100的倍数且是400的倍数是世纪闰年
year=int(input('year='))
result=year%4==0 and year%100!=0 or year%100==0 and year%400==0
print(result)

字符串

: 用单引号或者双引号括起来的内容,这两者是毫无区别的,但这只是单行的字符串,若是多行的字符串,需要用’‘’ 内容’‘’,或者"“” 内容 “”"这样表示

1
2
3
4
5
6
7
8
9
# 字符串
m1 = "my name is bond,James bond"
print(m1)
m2 = '''
A:What's your name?
B:My name is bond,James bond.Very nice to see you!
Comment:Salute to the 007,the famous character in a series of movies.
'''
print(m2)

format():格式化函数

1
2
3
4
5
6
7
8
9
10
11
12
13
name='tl'
book='bite of python '
# 使用索引代替位置,python索引从0开始
print('{0} wrote the {1}'.format(name,book))
# 直接使用大括号{}占用位置
print('{} wrote the {}'.format(name,book))
# 直接在format后面定义变量,但是前面的变量用大括号括起来
print('{name} wrote the {book}'.format(name='tlin',book='python'))
# format还可以进行保留小数点后几位,关键在前面的:.nf
print('{:.2f}'.format(1.0/3)) #保留两位
print('{:.5f}'.format(1.0/3)) #保留五位
# format还可以进行补充字符长度操作
print('{:-^8}'.format('hello')) # ^声明进行补充,用-补充8位

转义序列

对于字符串(string)中自带的 ’ " \等,若需要显示出来,则需要在符号前加一个反斜杠 \,一些重要的转义符,\n:转到下一行 \t: 制表符,但若在字符串的结尾打上,下一行接着写,则表示换行写入字符串,但仍然显示在同一行中。
注:在字符串前加上r或者R表示该字符串是原始(raw)字符串,加上转义字符后,也不会有任何效果,正如其名一样,非常地原始,不会经过任何处理。

1
2
3
4
5
6
7
8
# 转义字符
m3='I am \'a\' student '
print(m3)
m4='you are my son. \
hahahahhaah '
# m4=r'you are my son. \
#hahahahhaah\t '
print(m4)

变量

正如其名,是可以变化的量,我们之前讨论的都是字面常量,是无法改变的。
命名规范:变量是标识符的一种,
标识符的命名需要遵守:

  • 开头第一个字符是大小写字母,unicode字符或者下划线 _
  • 标识符的其他部分可以是大小写字母或者unicode字符,数字,下划线
  • 标识符命名会区分大小写,只要有一个字符大小写不一致,那么这两个就是不一样的变量

选择结构的使用:

在Python中语句从上往下地实现,这是顺序结构。但光靠顺序结构是无法实现众多的功能,例如,游戏中,分数大于1000分,则game over,若小等于,则重来。又例如,若去餐厅吃饭,付费超过1000元,可成为vip,若小等于,则不行。生活中有许多这样的例子,这样的结构叫做选择结构。在语法上,运用关键词if…else…,多条件时,用if…elif…else…,下面通过例子看怎么用选择结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# spyder中ctrl+1 单行注释;ctrl+4 多行注释
# 用户名是admin且密码是123456则身份验证成功否则身份验证失败
# 单if...else...使用
username=input('username=')
password=input('password=')
if username=='admin' and password=='123456':
print('身份验证成功')
else:
print('身份验证失败')



# 选择多分支结构
# 3x - 5 (x > 1)
# f(x) = x + 2 (-1 <= x <= 1)
# 5x + 3 (x < -1)
x=float(input('x='))
if x>1:
y=3*x-5
elif x>=-1:
y=x+2
else:
y=5*x+3
print('f(%.2f)=%.2f'%(x,y))
1
2
3
4
5
6
7
8
9
10
# 英制单位英寸与公制单位厘米互换。
# 查百度得知: 1 in= 2.54cm
value=float(input('value='))
unit=input('unit=inch/cm:')
if unit=='inch':
y=value*2.54
print('%.2f in=%.2f cm'%(value,y))
else:
y=value/2.54
print('%.2f cm=%.2f in'%(value,y))
1
2
3
4
5
6
7
8
9
10
11
12
#要求:如果输入的成绩在90分以上(含90分)输出A;80分-90分(不含90分)输出B;70分-80分(不含80分)输出C;60分-70分(不含70分)输出D;60分以下输出E。
grade=float(input('grade='))
if grade>=90:
print('A')
elif grade>=80:
print('B')
elif grade>=70:
print('C')
elif grade>=60:
print('D')
else:
print('E')
1
2
3
4
5
6
7
8
9
10
11
12
13
# 判断输入的边长能否构成三角形,如果能则计算出三角形的周长和面积
import math as mt
a=float(input('a='))
b=float(input('b='))
c=float(input('c='))
if a+b>c and a+c>b and b+c>a:
print('三条边%f,%f,%f可以构成三角形'%(a,b,c))
p=(a+b+c)/2
print('三角形周长为%f'%(p*2))
s=mt.sqrt(p*(p-a)*(p-b)*(p-c))
print('三角形的面积为%f'%(s))
else:
print('三条边无法构成三角形')

循环结构的使用(loop)

循环结构就是让程序中的某些指令或者代码片段重复执行的结构,有以下两种做法:for…in…结构和while循环

  • for…in…:如果明确的知道循环执行的次数或者要对一个容器进行迭代(后面会讲到),那么我们推荐使用for-in循环
    :::info
    range的用法:
    range(101):可以用来产生0到100范围的整数,需要注意的是取不到101。
    range(1, 101):可以用来产生1到100范围的整数,相当于前面是闭区间后面是开区间。
    range(1, 101, 2):可以用来产生1到100的奇数,其中2是步长,即每次数值递增的值。
    range(100, 0, -2):可以用来产生100到1的偶数,其中-2是步长,即每次数字递减的值。
    :::
1
2
3
4
5
# 对1....100进行求和
sum=0
for i in range(101):
sum+=i
print(sum)
1
2
3
4
5
# 对1....100间偶数求和
sum=0
for i in range(100,0,-2):
sum+=i
print(sum)
  • while循环:如果要构造不知道具体循环次数的循环结构,我们推荐使用while循环。while循环通过一个能够产生或转换出bool值的表达式来控制循环,表达式的值为True则继续循环;表达式的值为False则结束循环。
    :::info
    下面讲一下:break和continue的区别: break是跳出本个循环,不再执行循环内容,continue是跳出本轮循环,继续循环
    :::
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 猜数字游戏
import random as rd
answer=rd.randint(1, 100) #randint(a,b)是 a=<x<=b
counter=0
while True:
number=int(input('your choice is :'))
counter+=1
if number>answer:
print('小一点')
elif number<answer:
print('大一点')
else:
print('you are right')
break
print('you have guessed %d results'%(counter))
if counter>7:
print('you need to eat more food,hahaha')
else:
print('you are so smart,you have mastered the secret of playing this game')
1
2
3
4
5
# 输出九九乘法表
for i in range(1,10):
for j in range(1,i+1):
print('%d*%d=%d'%(i,j,i*j),end='\t')
print()

下面有三个小练习:

1
2
3
4
5
6
7
8
9
number=int(input('number='))
counter=0
for i in range(1,number+1):
if number%i==0:
counter+=1
if counter<=2 and number!=1:
print('%d is a prime number'%(number))
else:
print('%d isn\'t a prime number')
1
2
3
4
5
6
7
8
9
if a>=b:
a,b=a,b
else:
a,b=b,a
for i in range(a,1,-1):
if a%i==0 and b%i==0:
max=i
print('The greatest common divisor of %d and %d is %d '%(a,b,max))
print('The least common multiple of %d and %d is %d '%(a,b,a*b/max))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#     *
# **
# ***
# ****
# *****

# *
# **
# ***
# ****
# *****
# *
# ***
# *****
# *******
# *********
for i in range(5,0,-1):
for j in range(1,i):
print(' ',end='')
for j in range(5,i-1,-1):
print('*',end='')
print()

for i in range(5,0,-1):
for j in range(5,i-1,-1):
print('*',end='')
print()

for i in range(5,0,-1):
for j in range(1,i):
print(' ',end='')
for j in range(5,i-1,-1):
print('*',end='')
for j in range(5,i,-1):
print('*',end='')
print()

循环经典案例

1
2
3
4
5
6
7
8
# 说明:水仙花数也被称为超完全数字不变数、自恋数、自幂数、阿姆斯特朗数,它是一个3位数,
# 该数字每个位上数字的立方之和正好等于它本身,例如:$1^3 + 5^3+ 3^3=153$。
for i in range(100,1000):
a=i%10
b=i//10%10
c=(i-a-b*10)/100
if a**3+b**3+c**3==i:
print(i)
1
2
3
4
5
6
7
# 正整数的反转
num=int(input('number is :'))
reversed_number=0
while num>0:
reversed_number=reversed_number*10+num%10
num//=10
print(reversed_number)
1
2
3
4
5
6
7
8
9
10
11
# 百钱白鸡问题
# 说明:百钱百鸡是我国古代数学家张丘建在《算经》一书中提出的数学问题:
# 鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?
# 翻译成现代文是:公鸡5元一只,母鸡3元一只,小鸡1元三只,用100块钱买一百只鸡,
# 问公鸡、母鸡、小鸡各有多少只?
# 假设公鸡有x,母鸡有y,小鸡有z,满足数量关系和钱数关系
for x in range(0,21):
for y in range(0,34):
for z in range(0,101):
if x+y+z==100 and 5*x+3*y+z/3==100:
print(x,y,z)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# CRAPS赌博游戏
from random import randint
# 游戏初始化资金
money=int(input('your property is '))
start=True
while start:
start=False
# 设置赌资
debt=int(input('your debt chosen is : '))
if debt>money or debt<0:
break
# 游戏开始
first=randint(1,6)+randint(1, 6)
if first==7 or first==11:
print('you win')
money+=debt
elif first==2 or first==3 or first==12:
print('the makers win,you lost')
money-=debt
else:
go_on=True
while go_on:
go_on=False
num=randint(1,6)+randint(1, 6)
if num==7:
print('you lost')
money-=debt
elif num==first:
print('you win')
money+=debt
else:
go_on=True
print('your property is now %d'%(money))
a=int(input('please choose to go on(1) or give up(0) :'))
if a==1 and money!=0:
start=True
elif a==0:
start=False
print('you can control yourself,you are worth to be learned by me,the maker of the game')
else:
start=False
print('I am sorry to tell you that you have lost yourself, be wise')

循环习题

1
2
3
4
5
6
7
8
# 输出一百以内的素数
for i in range(1,100):
counter=0
for j in range(1,i+1):
if i%j==0:
counter+=1
if counter <= 2 and i != 1:
print(i)
1
2
3
4
5
6
7
8
9
10
# 找出10000以内的完美数。
# 说明:完美数又称为完全数或完备数,它的所有的真因子(即除了自身以外的因子)的和(即因子函数)恰好等于它本身。
# 例如:6($6=1+2+3$)和28($28=1+2+4+7+14$)就是完美数。完美数有很多神奇的特性,有兴趣的可以自行了解。
for i in range(2,10000):
sum=0
for j in range(1,i):
if i%j==0:
sum+=j
if sum==i:
print(i)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 斐波那契数列的生成
a=1
b=1
print(a)
print(b)
counter = 0
go_on = True
while go_on:
go_on=False
c=a+b
print(c)
counter+=1
a,b=b,c
if counter<18:
go_on=True
else:
go_on=False

函数:

编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”我们可以把那些重复的代码写成一个函数,来实现这个重复的功能,只需要调用输入参数即可,在python中函数的定义用def关键字来定义,函数也需要进行命名,其命名规则和变量的命名规则是一致的,其后面可以输入参数,就是数学里面的函数的自变量,在函数的最后需要用return这个关键字来进行返回值,下面用例子来进一步加深对该定义的理解:

1
2
3
4
5
6
7
8
9
10
def fac(m):
# 用def定义函数,fac是函数名,()里面是输入的参数
# 求阶乘,m!
a=1
for i in range(1,m+1):
a*=i
#用return来返回变量值
return a
m=int(input('m='))
print(fac(m))

在Python的Math库中有factorial这个求阶乘的函数,以后要用直接调用即可

在python中,如果一个函数要返回两个或者多个不同的值,可以采用返回元组,即return (a,b),在调用函数时,直接a1,b1=function(),就会把return里面的a赋值给a1,b给b1

  • 函数的参数: 在python中函数的参数有默认值也有可变参数,默认值是自己设计函数时定义的,而当自己输入值时,可以改为可变参数,当不输入任何参数时,默认使用函数定义的参数,即默认参数,不需要像其他语言一样支持函数的重载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from random import randint
def roll_dice(n=2):
num=0
for i in range(n):
num+=randint(1, 6)
return num
roll_dice() #不带任何参数
roll_dice(5) #输入参数5

def add(a=0,b=0,c=0):
return a+2*b+3*c
add() #不带参数
add(1,2,3) #输入参数,分别对应 1 2 3
add(b=5,c=2,a=5) # 不按顺序输入参数取值
  • 可变参数: 当设计函数时,不知道有多少个参数时,需要使用可变参数,在变量面前加号,表示该参数是可变参数,加了,表示多余的变量会被元组形式所接收,但若在变量之前加双**号,则是表示该变量会接受字典,下面用代码来说明:
1
2
3
4
5
6
7
def add(*a):  #定义a是可变参数
sum=0
for i in a:
sum+=i
return sum
print(add(1,2,3))
print(add(2,3))

用模块管理函数:

由于python没有函数重载的概念,对于同名的函数,先后定义,后面函数会覆盖前面的函数,因此在不同的模块(一个文件就是一个模块,module)使用import载入该函数,例如在module1.py中定义foo函数,在module2.py中定义foo函数,from module1 import foo as m1和from module2 import foo as m2,分别执行是不会发生冲突的,或者直接import module1 as m1,再用m1.foo(),用点函数运算操作

注: 若需要import的文件中含有可执行的代码,使用import时会运行该代码,有的时候我们不需要运行,可以把可执行代码放在if name='main:'语句后面,因为module有个隐藏的变量名叫做__name__,只有直接在该文件中运行时,name=main,然后才可以执行,而直接用import调用时,module的__name__时该module名,故if语句后面的可执行代码时不会运行的.

模块管理函数练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def gcd(x, y):
# 求最大公因数
if x < y:
x, y = y, x
factor = 0
for i in range(x, 0, -1):
if x % i == 0 and y % i == 0:
factor = i
break
return factor


def scm(x, y):
# 求最小公倍数
a = gcd(x, y)
return x*y/a


def hws(num):
# 判断是否是回文数
temp=num
reversed_num = 0
while temp > 0:
reversed_num = reversed_num*10+temp % 10
temp //= 10
return num==reversed_num

def is_prime(num):
# 判断是否质数
counter=0
if num==1:
return False
else:
# 判断因子个数
for i in range(1,num+1):
if num%i==0:
counter+=1
if counter>2:
return False
else:
return True
if __name__=='__main__':
num=int(input('the number is '))
if is_prime(num) and hws(num):
print('%d is a prime number and a palindromic number'%(num))
else:
print('it isn\'t a prime number and a palindromic number' )

函数的作用域

函数搜寻变量会按照局部作用域,嵌套作用域,全局作用域,内置作用域的顺续来寻找变量,函数里面的变量是无法影响到函数之外的变量,函数里面的函数的变量是无法影响到外层函数的变量的,但是可以使用关键字global,nonlocal来使得嵌套作用域,局部作用域的变量升级位全局作用域,嵌套作用域。我们可以看到全局作用域的变量影响范围是非常大的,我们需要尽可能减少全局变量的使用,为此,以后代码尽可能像下面这样写:

1
2
3
4
5
6
7
8
9
10
def main():
# =============================================================================
# your code
# =============================================================================
pass
#pass 是一种空操作(null operation)
#解释器执行到它的时候,除了检查语法是否合法,什么也不做就直接跳过。

if __name__=='__main__':
main()

这样写主要是为了减少全局变量的使用,减少对其它代码的影响

数据结构

列表(list) 元组(tuple) 字典(dictionary) 集合(set)通常包括以上这几种数据结构(data structure),列表使用中括号[ , ,]进行生成,元组用( , )进行生成,这里特别需要注意,如果这是一个空列表,mylist=(),但是如果列表只有一个元素,那么在那唯一一个元素之后需要加上逗号,以表示这是一个元组(tuple),例如,only_one_element_tuple=(‘a’, ),而字典是有键值对生成的,用大括号{},括起来的一个结构,{键值1:值1,键值2:值2, },键值是唯一的,用不可变的量进行表示,而值是可变或者不可变的,都可以,更重要的是,这写数据结构是可以进行切片操作,或者叫做索引(indexing),下面会用例子进行进一步地解释这些structure

  • 字符串:把零个或者多个字符用单引号或者双引号括起来形成的,即形成了字符串,在字符串的输出中,可以利用转义符,+某些字母,\n,\t等等,\也可以加一些unicode字符来产生中文等,https://www.ifreesite.com/unicode-ascii-ansi.htm这个链接可以进行中文和Unicode字符互换,之外,若不想要\进行转义,可在字符串的最前端加上r或者R,字符串进行运算符操作,+可进行字符串的拼接,而*可以进行字符串的重复,*n即是重复n次,用in or not in 来进行bool运算,以及用[],或者[:]进行切片操作
1
2
3
4
5
6
7
8
a='hello'
b='world'
print(a+b) #字符串的拼接
print(a*2+b*8) #字符串的重复
print('he' in a) #字符串的属于与不属于关系
print('a' not in a)
print(a[:3])
print(a[::2])

除了运算符之外,字符串还可以进行函数操作,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
str1 = 'hello, world!'
# 通过内置函数len计算字符串的长度
print(len(str1)) # 13
# 获得字符串首字母大写的拷贝
print(str1.capitalize()) # Hello, world!
# 获得字符串每个单词首字母大写的拷贝
print(str1.title()) # Hello, World!
# 获得字符串变大写后的拷贝
print(str1.upper()) # HELLO, WORLD!
# 从字符串中查找子串所在位置
print(str1.find('or')) # 8
print(str1.find('shit')) # -1
# 与find类似但找不到子串时会引发异常
# print(str1.index('or'))
# print(str1.index('shit'))
# 检查字符串是否以指定的字符串开头
print(str1.startswith('He')) # False
print(str1.startswith('hel')) # True
# 检查字符串是否以指定的字符串结尾
print(str1.endswith('!')) # True
# 将字符串以指定的宽度居中并在两侧填充指定的字符
print(str1.center(50, '*'))
# 将字符串以指定的宽度靠右放置左侧填充指定的字符
print(str1.rjust(50, ' '))
str2 = 'abc123456'
# 检查字符串是否由数字构成
print(str2.isdigit()) # False, is digit
# 检查字符串是否以字母构成
print(str2.isalpha()) # False, is alpha
# 检查字符串是否以数字和字母构成
print(str2.isalnum()) # True, is alpha and number
str3 = ' jackfrued@126.com '
print(str3)
# 获得字符串修剪左右两侧空格之后的拷贝
print(str3.strip())

关于格式化输出,在python3.6之后,可以直接在字符串前面加f来进行输出,例如

1
2
3
a=5
b=6
print(f'{a}*{b}={a*b}')
  • 列表 :列表是结构化的,非标量类型,是值的有序序列,可以通过索引进行标识,定义列表可以用[]括起来,列表内元素用逗号隔开,可以使用for循环对列表进行遍历,也可以使用[],[:]取出列表中的一个或者多个元素,下面用代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def main():
list1 = [1, 2, 1, 5, 7]
print(list1) # 打印出list1的全部元素
list2 = ['hello']*3 # list2的元素重复3遍
print(list2)
print(len(list2)) # len返回list2的元素个数
# 列表索引计算
print(list1[0]) # 1
print(list1[3]) # 5
print(list1[-1])
print(list1[-3])
# 利用索引进行替换
list1[2] = 100
print(list1)
# 通过for循环用下标遍历元素
for i in range(len(list1)):
print(list1[i])
# 还可以直接用for循环遍历列表元素
for i in list1:
print(i)
# 同时,也还可以用enumerate函数把lis1列表的全部元素给遍历出来
for i, ele in enumerate(list1): # enumerate(obj,start)enumerate里面是有两个参数
#
print(i, ele)
pass
if __name__ == '__main__':
main()

向列表中添加元素或者删除元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
list1=[1,2,3,7,89]
# 向列表中添加元素,可以用append(向列表末尾追加元素)和insert(向指定索引位置插入元素),
list1.append(10)
print(list1)
list1.insert(1, 5)
# 合并两个列表,可以用.extend(list) or 直接+list
list2=list1+[789,456]
print(list2)
print(len(list2))
# 先通过成员运算判断元素是否在列表中,如果存在就删除该元素
if 789 in list2:
list2.remove(789)
if 256 not in list2:
list2.append(256)
# 从指定的位置删除元素,利用pop()函数
print(list2)
list2.pop(0)
list2.pop(len(list2)-1) #pop
print(list2
1
2
3
4
5
6
7
8
9
10
# 实现列表的切片操作
list1=['apple','orange','banana','strawberry']
list1+=['watermelon','blueberry']
list2=sorted(list1)
list3=sorted(list1,key=len) #按长度排序
list4=sorted(list1,reverse=True) #字母倒序
print(list1)
print(list2)
print(list3)
print(list4)

生成式和生成器: 列表可以使用生成式语法来产生(如下所示),此外,还可以用yield将一个函数改为生成器函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
import sys
f=[x for x in range(101)] #这是列表的生成式语法
print(f)
f=[x+y for x in 'ABCDE' for y in '12345']
print(f)
f=[x**2 for x in range(11)]
print(sys.getsizeof(f)) #184
print(f)
f=(x**2 for x in range(11)) #这是一个生成器,用小括号括起来,占用更少的内存资源
print(sys.getsizeof(f)) #112
print(f)
for i in f:
print(i)
1
2
3
4
5
6
7
8
# 产生生成器函数的另一种方法,用yield关键字代替return进行声明
def fb(n):
a,b=0,1
for _ in range(n):
a,b=b,a+b
yield a #这里用yield代替了return
for i in fb(20):
print(i)
  • 元组:元组也是一种容器数据类型,可以用一个变量来存储多个数据,但和列表不同的是,元组(tuple)是不能改变的,下面用代码演示
1
2
3
4
5
6
7
8
9
10
11
12

tp=('t',2,True,456)
print(tp)
print(tp[0])
# tp[0]=2
print(tp[0])
# 元组可以用list转化为列表
lp=list(tp)
print(lp)
# 列表也可以用tuple进行元组化
tp=tuple(lp)
print(tp)

使用元组的优点:
元组中的元素是无法修改的,事实上我们在项目中尤其是多线程环境(后面会讲到)中可能更喜欢使用的是那些不变对象(一方面因为对象状态不能修改,所以可以避免由此引起的不必要的程序错误,简单的说就是一个不变的对象要比可变的对象更加容易维护;另一方面因为没有任何一个线程能够修改不变对象的内部状态,一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销。一个不变对象可以方便的被共享访问)。所以结论就是:如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,当然如果一个方法要返回多个值,使用元组也是不错的选择。

  • 集合:set 用{}包含元素,用法可以见下面代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 集合生成语法,用大括号括起来
s1={1,2,3}
print(s1)
print('the set\'s length is',len(s1))
# 用set()函数转化成集合
s2=set(range(1,10))
s3=set((1,2,3,4))
print(s2,s3)
# 用集合推导式生成集合
s4={num for num in range(10) if num%3==0 or num%5==0}
print(s4)
# 集合的添加与删除元素,remove删除一定存在的元素,discard包容性更强,也是删除元素
s1.add(15) #加入元素
s1.add(16)
s1.update([19,15]) #对集合更新,()里面是需要更新的内容,若不存在,则添加,若存在,则跳过,可以是列表,也可以是集合,元组也ok
s1.update({20,15})
s1.update(tuple({20,18}))
# 当然集合也可以进行交并补差 &交 |并 -差 ^对称差,<=子集,包含于 >= 包含
  • 字典:dictionary :字典是另一种可变容器模型,Python中的字典跟我们生活中使用的字典是一样一样的,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。字典是可以被更新的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 创建字典的字面量语法
d1={'谭霖':100,'张三':90,'李四':89}
print(d1)
items1=dict(one=1,two=2,three=3) #构造字典函数,dict()
print(items1)
# 创建字典的推导式语法
d2={num:num**2 for num in range(11)}
print(d2)
# 可以通过键获的字典的值value
print(d1['谭霖'])
d1['张三']=85
d1['李四']=98
print(d1)
d1.update(王五=79,张九=88)
print(d1.popitem()) #清除某个字段
print(d1.pop('王五'))
d1.clear() #清空字典
print(d1)

数据结构练习

在屏幕上显示跑马灯文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def main():
import os
import time
content = '北京欢迎你,你的到来是我莫大的荣幸-------'
go_on = True
while go_on:
# 清理屏幕上的输出
os.system('cls')
print(content)
# 延迟0.2s
time.sleep(0.2)
content = content[1:]+content[0]
pass


if __name__ == '__main__':
main()

设计一个函数产生指定长度的验证码,验证码由大小写字母和数字构成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def generate_coed(n):
'''
Parameters
----------
n : TYPE, optional
DESCRIPTION. The default is 4.
Returns 一个由数字和大小写字母组成验证码
-------
None.
'''
import random
choice='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
num=len(choice)
code=''
for i in range(n):
code+=choice[random.randint(1,num)]
return code
print(generate_coed(5))

设计一个函数返回给定文件名的后缀名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def get_suffix(filename,has_dot=False):
'''
读取文件的后缀名

Parameters
----------
filename : TYPE
DESCRIPTION. 文件名
has_dot : TYPE, optional
DESCRIPTION. The default is False.

Returns 文件的后缀名
-------
None.

'''
pos=filename.find('.')
num=len(filename)
if 0 < pos < num-1:
index=pos if has_dot else pos+1
return filename[index:]
else:
return ''
pass
get_suffix('game.py')
get_suffix('game.py',has_dot=(True))

设计一个函数返回传入的列表中最大和第二大的元素的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
def get_max_secmax(list):
'''
返回列表中最大和第二大的值

Parameters
----------
list : TYPE :this is a list,and all of elements are numbers
DESCRIPTION.

Returns the greatest and second greatest numbers
-------
None.

'''
num=len(list)
largest=list[0]
for i in range(num):
if list[i]>=largest:
largest=list[i]
list2=list[:]
list2.remove(largest)
sec_largest=list2[0]
for i in range(len(list2)):
if list[i]>=sec_largest:
sec_largest=list[i]
return largest,sec_largest
pass
get_max_secmax([1,5,7,911,1500])

计算指定的年月日是这一年的第几天

1
2
3
4
5
6
7
8
9
10
11
12
def is_leap_year(year):
return year%100!=0 and year%4==0 or year %100==0 and year %400==0
pass
def return_day(year,month,day):
days=[[31,28,31,30,31,30,31,31,30,31,30,31],[31,29,31,30,31,30,31,31,30,31,30,31]]
year_days=days[1] if is_leap_year else days[2]
sum=0
for i in range(month-1):
sum+=year_days[i]
print(f'这是{year}的第{sum+day}天')
return_day(2020,5,25)
return_day(2020,1,1)

杨辉三角

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 实现杨辉三角
def yanghui(n):
lt=[[]]*n
for row in range(n):
lt[row]=[None]*(row+1)
for col in range(len(lt[row])):
if col==0 or col==row:
lt[row][col]=1
else:
lt[row][col]=lt[row-1][col-1]+lt[row-1][col]
for i in range(n):
x=str(lt[i])
print(x.center(n*10))

pass

yanghui(10)

读取文件

  1. 内键open()方法:open函数用open(filename,mode),其中mode包括以下几种类型:

‘r’:只读 ’‘w’:新建,只写入 ,用一次就会覆盖之前的内容‘a’:可以添加数据到末尾 ‘r+’:打开文件可写,可读

1
2
3
4
5
# create a file
file=open('file1.txt','w')
file.write('my name is tanlin.')
file.write('what\'s your name?')
file.close()
1
2
3
4
5
6
7
8
9
10
11
# read a file
file = open('file1.txt','r') #注意,这里的mode是r
#show whole efile
print(file.read())
#show first ten characterrs
print(file.read(10))
#view by line
print(file.readline())
#view all by line
print(file.readlines())
file.close()
1
2
3
4
file=open('file1.txt','r+')
for i in file:
print(i)
file.close()

注:在进行完文件的操作后,要进行文件的关闭,file.close() ,由于很容易打开文件之后忘记关闭文件,所以最好用with方法,即with open(‘filename’,‘mode’) as f:

1
2
3
4
5
6
file = open('file1.txt', 'a',encoding='UTF-8')
file.write('\nI am back again. \n')
file.write('Do you miss me?\n')
file.close()
file = open('file1.txt', 'r+',encoding='UTF-8')
print(file.read())
  1. 文件路径:可以使用绝对路径,例如windows,open(‘C:\Users\user\Documents\file.txt’),但是字符串需要转义,所以\需要写两次

Jupyter Notebook

快捷键以及magic功能

快捷键

  1. 在单元格内,按shift+enter执行代码,并移至下一单元格,输入时,单元格四周为绿色,一个界面的代码对象可以共用,也就是上一个单元格的对象可以被下面单元格调用
  2. notebook有自动保存功能,但也可以按快捷键ctrl+s进行保存
  3. tab键:可以起到模糊查找的功能,输入对象的某几个字母后,按下tab键,字母后边会产生一个下拉框,可进行选择类的实例,函数,固定变量,实例的方法及属性内容,此外,tab还可以查找指定路径下的文件,在单元格内输入path=’ 绝对路径’,按下tab后,会产生文件名,但要注意的是,python中的路径是用,而notebook在用tab查找时,要用,但若在notebook执行代码需要用到路径,用\或/都是可以的。
  4. 单元格有两种编辑类型,一种是代码编辑类型,一种是markdown类型,按ctrl+M可以把代码编辑类型转为markdown类型
  5. 中断死循环代码运行可以用esc+i+i来进行

魔法命令

常用行魔法命令(%开头)

  1. %load source,source为指定路径下的python文件,输入%load source后,按下enter键,进行导入代码,该方法也可以导入网络上的代码,%load+网址/文件名
  2. %run可以直接运行.py格式的python代码文件

常用的单元魔法命令(%%开头)

  1. %%内核名,可以直接进行混合编程,%%latex进行latex代码编写,等等
  2. %%time 测试单元格代码单次执行时间,并返回测试结果
  3. %%timeit,测试单元格执行n次的平均时间

Package的学习

主要学习数据处理,科学计算以及绘图的几个库,numpy,pandas,scipy,matplotlib

Numpy(Numerical Python)

借助多维数组和相关的函数进行辅助的数据处理和计算,即numpy的核心是环绕ndarry(N维数组)和ufunc(universal function标准函数)两个类对象实现相关的计算功能。

array

numpy的数组是一系列网格值,可以被一个非负整数组成的二维列表进行索引,利用shape可以返回每一个方向上的数组的大小一起组成的一个元组,初始化numpy的array用列表进行嵌套

1
2
3
4
5
6
7
8
9
10
a=np.array([1,2,3,4])
print(type(a))
print(a[0],a[1])
a[0]=9
print(a)
# 构建一个多维数组,不过需要用[]括起来,里面任然是一个列表
b=np.array([[1,2,3],[2,5,7]])#这只是二维数组
c=np.array([[[1,2],[3,4]],[[5,6],[7,8]]])
print(b.shape)
print(b[0,0],b[1,2])

注:

  1. 数组维数快速识别,开始如为[[[(三个左中括号),则是三维数组,开始若为[[[[(四个左中括号),则是四维数组
  2. 数维数时,从最里面往外面数,如[[[,最里面[为第一维,往左第二个[是第二维,最左边为第三维
  3. 数组可以表示数据的维度,也可以表示层级分类的类别数

numpy中创建数组的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import numpy as np
# 构建一个1维数组
a=np.array([1,2,3,4])
print(type(a))
print(a[0],a[1])
a[0]=9
print(a)
# 构建一个多维数组,不过需要用[]括起来,里面任然是一个列表
b=np.array([[1,2,3],[2,5,7]])
print(b.shape)
print(b[0,0],b[1,2])
# numpy中创建数组的函数
# 创建全零矩阵
a=np.zeros((5,2))
print(a)
# 创建全1矩阵
b=np.ones((4,4))
print(b)
# 创建一个全是某个常数的数组
c=np.full((3,3), 5)
print(c)
# 创建单位矩阵
d=np.eye((5))
print(d)
# 创建随机矩阵
e=np.random.random((2,2))
print(e)
# np.arange(start,end,step value)和np.linspace(start,end,num)
# 区别:arange那里是不会包括end值,linspace那里包括了end值
#linspace的步长由起始点,终点,和点的数量num决定,步长=(终点-起始点)/(num-1)

一些其他的函数:

  1. repeat():建立每个元素重复n次的函数,格式为repeat(a,repeats,axis=none),a为重复的数组,repeats为元素重复的个数,axis为数组重复的维度
数组对象的属性
  1. ndim: 返回数组的维点
  2. shape: 返回数组的形状
  3. dtype: 返回数组元素的类型
  4. size: 返回数组元素的总个数
数组对象的方法
  1. reshape(x,y)方法:改变数组形状,但x*y等于被改变的数组的size
  2. all()方法:判断数组的元素是否全为0,括号里面可以加axis=0或1,若axis=0,则是以列向看,axis=1,则是从行向看。但是它只适用于数字元素数组了。
  3. any()方法:判断数组元素是否存在非零的,若有,则返回true,若全为0,则返回,和all()差不多
1
2
3
4
5
6
import numpy as np
a=np.array([1,2,3,4,5,6])
b=a.reshape(3,2)
print(b)
c=b.all(axis=1)
print(c)
  1. copy()方法: 若直接进行赋值,如b=a,只是把a的地址传给了b,若改变a或者b,另一个也会发生改变;但这里使用copy方法,则不会相互影响
1
2
3
4
5
6
7
8
9
# 直接使用赋值
c=a
print(c,a)
c[0]=10
print(c,a)
# 使用copy方法
c=a.copy()
c[0]=15
print(c,a)

注: 数组的方法和属性的区别之一是,方法后面需要带一对括号,属性则不用

数组索引

python提供了许多种索引的方式

  1. 切片:类似于list的切片,但是array是multi—dimension的,必须为每一个维度指定一个切片
1
2
3
4
5
6
7
8
9
10
11
12
a=np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
b=a[:2,1:3]
print(b)
print(a[0,1])
b[0,0]=77
print(a[0,1])
# 整数索引与切片索引相混合
row_r1=a[1,:] #这是一个array,但是shape是(4,)
print(type(row_r1),row_r1,row_r1.shape)
row_r2=a[1:2,:] #这是一个array,但是shape是(1,4)
print(type(row_r2),row_r2,row_r2.shape)
# 这种做法对column也是同样适用的
  1. 整数数组索引: 索引的两个维度里面都放数组,array进行索引时,会对数组分别一一对应进行查找,例如:a[[0,0],[1,5]],其实查找的时a[0,1],a[0,5],利用这个可以访问一个数组里面的每一行(列)的任意一个数,放在一起形成一个array,见下面的例子
1
2
3
4
5
6
a=np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
print(a[[0,1,2],[0,1,0]])
# 若想找每行中的第2个数
print(a[np.arange(3),np.full((1,3), 1)])
# 当然也可以进行计算
a[np.arange(3),np.full((1,3), 1)]+=10

3.布尔数组索引: 布尔数组索引通常可以挑选出我们想要的任何满足某种条件的数据

1
2
3
4
5
import numpy as np
a=np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])
a_index=a>10
print(a_index) #返回一个Boolean型的和a.shape一致大小的数组
print(a[a_index])

数据类型

对于每一个array,numpy会自动产生一个数据类型,保持一致,当然也可以指定,在用np.array([] , dtype= )来自己认为设置

numpy的四则运算,数学函数,以及数据操作

可以用下面的函数进行运算

np.add() or + 数组加法
np.substract() or - 数组减法
np.multiply() or * 数组乘法(指对应元素相乘)
np.divide() or / 数组除法(指对应元素相除)
% 取余
// 取整
np.sqrt() 对数组元素开根号

更特别的,如果要进行线性代数(高等代数)中的矩阵的乘法,可以用dot函数,在numpy和标准库的array,模块里都有内积的dot函数,所以有两种写法,用a.dot(b)或者np.dot(x,y),这里的x,y是数组(矩阵),但是要符合高代里的矩阵乘法的条件
对于numpy里面的sum求和,对于不同的axis,求和的方式有所区别,axis=0,是对列求和;axis=1,是对行求和,而不设置axis,则是对所有的元素求和。


数学初等函数
  1. 幂函数和指数函数

$y=x^n$,其中x为底,n为指数,当x为自变量,n不变时,是幂函数;当x不变,n为自变量时,时指数函数。
以e为底的指数函数,用np.exp(),若括号里是一个值,则返回一个值;若括号里是一个数组,则返回一个数组;
以2为底的指数函数,用np.exp2(),返回值与上面类似

  1. 对数函数

以e为底的对数,np.log()
以10为底的对数,np.log10()
以2为底的对数,np.log2()
再介绍一种函数,比log(1+x)具有更高精度的计算能力,log1p()函数,虽然其等价于数学里的log(1+x),但在计算机计算里,能准确反映出x到1e-17,可见其精度之高

  1. 三角函数与反三角函数

常见的三角函数,比如np.sin(),np.cos(),np.tan(),np.arcsin(),np.arccos,np.arctan()

随机函数
  1. random.rand(do,d1,d2,…,dn): 产生[0,1)范围内的浮点随机数,其中参数dn用于指定维度的长度
  2. random.randn(do,d1,d2,…,dn): 产生标准正态分布随机数,其中参数dn用于指定维度的长度
  3. random.randint(low,high,size):产生[low,high)范围内的整型随机数,其中size为产生数组的大小,一个整数就是元素个数且为一维,用元组表示为多维
  4. random.normal(mu=0,sigma=1,size):产生正态分布随机数,mu是期望,sigma是标准差,size是数组大小(包括维数)
  5. random.uniform(low,high,size): 产生指定范围[low,high)的均匀分布的随机数,size为数组大小,多维用元组表示
  6. random.poisson(lam=1,size=none):产生泊松分布随机数,lam为期望
  7. random.permutation(x):产生一个乱序的随机数组。若x是一个标量时,则返回指定范围[0,x)的乱序数组;若x是一个可变集合(数组或者列表),则对集合中的值进行乱序排列
  8. random.shuffle(x):直接对x数组进行乱序处理,但shuffle()并不会返回乱序后的数组,直接打印原数组即可,即是乱序后的数组
  9. random.choice(a,size=None,replace=True,p=None):这是随机抽取a中size个元素,可以重复抽取,这里a是被抽取的数组或者列表,size是被抽取元素个数,replace是能否重复抽取,True是可以重复,False是不可重复,p=[x,y,z]是为a中每个元素赋被抽取的权重
统计函数

基础统计函数

为了下述函数的充分理解,先介绍axis(轴)参数的使用,在多维数组统计时,axis=0表示按列进行统计,axis=1表示按行进行统计(下面所有的函数前面加个nan,可以忽略数组中的空值)

  1. np.sum(a,axis=None) 求和函数,a是一个待求和的数组,axis不设置值时,求所有元素的和;axis=0时,求每一列的和;axis=1时,求每一行的和。而对于数组中由nan(空值存在时),使用nansum进行求和,使用方法同sum
  2. np.prod(a,axis=None) 求乘积函数,a是个待求乘积的数组,axis不设置值时,对所有元素求乘积;axis=0,对每一列求乘积;axis=1,对每一行求乘积。同理,nanprod(a,axis=None)针对由nan的数组
  3. 对于求最大值,最小值函数,np.max(a,axis=None),np.min(a,axis=None),累计求和函数np.cumsum(a,axis=None),累计求乘积函数np.cumprod(a,axis=None),平均值函数np.mean(a,axis=None),np.average(a,axis=None,weights=None,returned=False),a时待求数组,axis为轴参数,weights时为(行或列)每个元素的赋的权重,和为1,returned为False时,以数组返回,returned为True时,以元组返回;np.median(a,axis=None)也是求均值
  4. np.var(),求方差,np.std() 求标准差,np.ptp(a,axis=None)求和或列的轴的最大值与最小值的差
  5. np.percentile(a,p,axis=None):求一个统计样本沿指定轴的第p个百分位数,axis是轴参数
  6. np.sort(a,axis=1):排序函数,其中a为需要统计的集合对象,axis=0,以列为单位进行升序排序;axis=1,以行为单位进行升序排列
  7. np.around(a,decimals=0):四舍五入函数,decimals为保留的小数位数
  8. np.floor(x) :取浮点数的整数部分
  9. np.ceil(x):对x向上取值
  10. np.rint(x):简单取最接近的整数
  11. np.abs(x):绝对值函数
  12. np.where(condition,x,y):条件比较函数,根据condition条件,选择x数组或者y数组里的元素,并以数组形式返回。当condition为true时,返回x中元素;若为false,返回y中元素

官方数学函数文档
numpy除了可以进行数学运算之外,还可以进行reshape(重塑)和manipulate(操纵)数组,转置的话可以用T这个数组的attribute,即a.T就是对a进行transpose(转置),下面这个是对于函数操纵的官方文档

数组操纵
  1. vstack((x1,x2,x3)):将数组垂直方向上连接起来

注:vstack连接的数组必须保持列数一致,且数组参数得用元组体现,数组参数个数不限

  1. hstack((x1,x2,x3)):将数组在水平方向上连接起来
  2. vsplit(ar,N):将数组在垂直方向上,进行水平分割,ar是要被分割的对象,而N是要被分成的块数
  3. hsplit(ar,N):将数组在水平方向上,进行垂直分割,ar是要被分割的对象,而N是要被分成的块数

注:这里的堆叠和分割可以联系起来记,vstack和vsplit是相反的操作

广播(broadcast)

广播是numpy进行数值计算的一个特殊机制(mechanism),对于形状(shape)相同的数组来说,进行数值运算自然没问题,但遇到一个小规模的数组与大规模的数组,会自动触发广播特性,在讨论之前,先看一个函数,np.tile(A,reps),其中reps可以是数字,也可以是元组,如果是数字,则默认是(1,d)这样的元组,tile是有瓷砖的意思,我们把数组A看做一块瓷砖,tuple的第一个数字是纵向平铺的个数,第二个数字是横向平铺的数字,看下图解析

广播对于操作的数组也是有局限性,他们需要满足更小的数组的一个维度是1,且剩余的其他维度与大数组应该保持一致,否则无法计算,广播其实就是把小数组用tile函数扩展成大数组的一样的shape才可以继续进行数值计算

面向对象编程(先不学)

什么是面向对象编程?

比较官方的说法是,“把一组数据结构和处理他们的方法组成对象(object),把具有相同行为的对象归纳为类(class),通过封装(encapsulation)隐藏类中方法实现的细节,再通过继承(inheritance)实现类的特化(specialization)和泛化(generzlization),通过多态(polymorphism)实现类的动态分配”

而比较通俗的说法是,面向对象编程是一种编程的理念,而按照这种编程理念,程序中的数据和操作数据的函数是一个逻辑上的整体,我们称之为“对象”,而我们解决问题的方式就是创建出需要的对象并向对象发出各种各样的消息,多个对象的协同工作最终可以让我们构造出复杂的系统来解决现实中的问题。

类和对象

类是对象的蓝图和模板,对象是类的实例,类是一个抽象的东西,而对象是一个具体的东西。在面向对象编程的世界中,一切皆为对象,对象都有属性(静态特征)和行为(动态特征),每个对象都是独一无二的,而且每个对象必定属于某一个类.

定义类和使用对象

在Python中,用关键字class来定义类,用函数来定义方法(动态特征),下面用代码创建一个类和对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Student(object):
# __init__(self,xx ,yy)用来创建对象self的属性xx和yy,并进行绑定
def __init__(self, name, age):
self.name = name
self.age = age
# 用函数定义study方法
def study(self, coursename):
print(f'{self.name}正在学习{coursename}')
# 用函数定义watch方法
def watch(self):
if self.age < 18:
print(f'{self.name}只能观看小儿科电视剧')
else:
print(f'{self.name}已经足够大了,可以观看岛国影片了,哈哈')
def main():
# 创建学生对象并赋予名字和年龄
stu1 = Student('谭霖', 21)
# 为stu1建立study方法
stu1.study('python')
stu1.watch()
stu2 = Student('宇宙', 16)
stu2.study('数学分析')
stu2.watch()
pass

if __name__ == '__main__':
main()

字段(field)中包含类变量和实例变量,类变量是在class中定义的全局变量,对类中的每一个方法都有作业,是作用于类与对象的,而实例变量是作用于实例的,是实例所独有的,不会被共享

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class robot:
# population是一个类变量
population = 0

def __init__(self, name):
self.name = name
print('initializing {}'.format(self.name))
robot.population += 1

def die(self):
print(f'{self.name} is being destroyed')
robot.population -= 1
if robot.population == 0:
print(f'{self.name} is the last robot')
else:
print(f'there are still {robot.population } robots')

def say_hi(self):
print('greetings, my master call me {}'.format(self.name))

@classmethod
def how_many(cls):
print(f'we have {cls.population } robots')


droid1 = robot("r2")
print(robot.population)
droid1.say_hi()
droid1.die()
robot.how_many()

访问可见性

在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,一般来说,对象的方法是公开的,可以直接访问,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头,下面的代码可以验证这一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Test():
def __init__(self, foo):
self.__foo = foo

def __bar(self):
print(self.__foo)
print('__bar')


def main():
test = Test('hello')
test.__bar()
print(test.__foo)


if __name__ == '__main__':
main()

但是python并没有在语法上严格设置私密性,只不过是给属性换了一个名字而已,当知道命名的规则(_类名__私有属性),仍然可以访问该私有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Test():
def __init__(self, foo):
self.__foo = foo

def __bar(self):
print(self.__foo)
print('__bar')


def main():
test = Test('hello')
test._Test__bar()
print(test._Test__foo)


if __name__ == '__main__':
main()

面向对象的支柱

面向对象有三大支柱:封装,继承,和多态。

封装(encapulation)

封装就是尽可能地编程地细节给隐藏,只暴露出简单的编程接口。我们在类中定义的方法其实就是把数据和对数据的操作封装起来了,在我们创建了对象之后,只需要给对象发送一个消息(调用方法)就可以执行方法中的代码,也就是说我们只需要知道方法的名字和传入的参数(方法的外部视图),而不需要知道方法内部的实现细节(方法的内部视图)。

继承(inheritance)

继承相当于对代码的重用,继承是在类之间实现类型于子类型的关系,把我们需要研究对象的特征分成公共特征或者独有特征,可以把公共特征设置为父类,独有特征设置为子类,子类可以继承父类的特征。父类发生变化,继承的子类同样也会变化,但子类变化不会影响父类

多态(polymorphism)

在任何情况下,如果父类希望,子类型可以被替代,可以看作子类型是父类的一个实例

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from time import sleep
class clock(object):
def __init__(self,hour=0,minute=0,second=0):
self._hour=hour
self._minute=minute
self._second=second
def run(self):
self._second+=1
if self._second==60:
self._second=0
self._minute+=1
if self._minute==60:
self.minute=0
self.hour+=1
if self.hour==24:
self.hour=0
def show(self):
return f'{self._hour}:{self._minute}:{self._second}'
def main():
tm=clock(5,25,56)
while True:
print(tm.show())
sleep(1)
tm.run()
if __name__=='__main__':
main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from math import sqrt
class point(object):
def __init__(self,x,y):
self.x=x
self.y=y
def move_to(self,x,y):
self.x=x
self.y=y
def move_by(self,dx,dy):
self.x+=dx
self.y+=dy
def distance(self,other):
dx=self.x-other.x
dy=self.y-other.y
return sqrt(dx**2+dy**2)
def __str__(self):
return '(%s,%s)'%(str(self.x),str(self.y))
def main():
p1=point(5, 5)
p2=point(0, 0)
print(p1)
print(p2)
p2.move_by(2, 5)
print(p2)
print(p1.distance(p2))
if __name__=='__main__':
main()