Python笔记-持续更新

Python笔记-持续更新

Scroll Down
1、平方运算符 **
2、异或运算符 ^
3、术语列表:

problem solving: 问题解决

high-level language: 高级语言

l ow-level language: 低级语言

portability: 可移植性

interpreter: 解释器

prompt: 提示符

operator: 运算符

integer: 浮点数

natural language: 自然语言

formal language: 公式语言

parse: 解释

variable:变量

assignment:赋值,给一个变量赋予值。

state diagram:状态图,图形化表征每个变量的值。

operand:运算数,运算符来进行运算操作的数值。

expression:表达式,一组变量、运算数的组合,会产生单值作为结果。

evaluate:求解,把表达式所表示的运算计算出来,得到一个单独的值。

statement:声明,一组表示一种命令或者动作的代码,目前我们了解的只有赋值语句和打印语句。

interactive mode:交互模式

execute:运行

order of operations:运算符优先级

concatenate:拼接,把两个运算对象相互连接到一起。

exception:异常,程序运行的时候被探测到的错误。

semantics:语义,程序的意义。

semantic error:语义错误,程序运行的结果和料想的不一样,没有完成设计的功能,而是干了点其他的事情。

4、变量名称不能以数字开头
5、一个声明语句的左边必须是变量名,任何其他的表达式放到等号左边都会导致语法错误(也有例外)
6、函数

def 函数名():

​ 函数体

第一行叫做函数头,函数名后面的()为空,表示这个函数不需要参数,函数体必须是相对于函数头部有缩进的,距离行首要有四个空格的距离。

在函数定义完毕的结尾,必须输入一行空白行,定义函数会创建一个函数类的对象。

函数之间可以嵌套使用,在一个函数的函数体中,可以使用另一个函数。

7、函数形式参数和实际参数

形参就是形式上的参数,可以理解为数学的X,没有实际的值,通过别人赋值后才有意义。相当于变量。

实参就是实际意义上的参数,是一个实际存在的参数,可以是字符串或是数字等。

# 函数头括号内的就是形式参数,要传递给它的值就是实际参数

def print_name(name,age):
    print("My name is" + name + ",I am " + age + "years old!")
    
# ① 默认参数
# 此种情况下,函数头部已经确定了要使用参数的值,调用函数时不需要再传值

# ② 位置实参 VAR_POSITIONAL
# 调用函数时,必须将函数调用中的每个实参都关联到函数定义中的一个形参。关联方式是基于实参的顺序,这被称为位置参数
print_name("zjib","22")

# ③ 关键字参数 VAR_KEYWORD
# 关键字实参是以等号的形式,直接将形参与实参关联起来,这样就不存在顺序的问题了。
print_name(name="zjib",age="22")

""" ④ 上述几种方式是有几个形参,就传进去几个实参,但有时候会不确定有多少个参数,它以一个 *加上形参名 的方式来表示这个函数的参数个数不定,可能为0个,可能为n个。注意,不管有多少个,在函数内部都被存放在以形参名为标识符的元组中,如下"""

def print_args(*args):
    print(args)
    
print_args(1,2,3)	√
(1, 2, 3)
print_args(args=1)	×

# ⑤ 形参名前加 两个* ,表示在函数内部将被存放在以形参名为标识符的字典dictionary中,这时调用函数的方法需要采用arg1=value1,arg2=value2这样的形式

def print_args(**args):
    print(args)
    
print_args(x=1,y=2,z=3)
{'x': 1, 'y': 2, 'z': 3}   
        

9、turtle模块

import turtle

t = turle.Turtle()

t.fd()

…………

fd 向前

bk 向后

rt 右转,角度为参数

lt 左转,角度为参数

10、for语句

头部后一定要用冒号,一个缩进的循环体

11、封装

用函数的形式把一段代码包装起来,叫做封装。这样有一个好处,就是给代码起了个名字,有类似于文档说明的功能,更好理解了。另外一个好处是下次重复使用这段代码的时候,再次调用函数就可以了,这比复制粘贴函数体方便多了。

import turtle
bob = turtle.Turtle()
def square(t):
    for i in range(4):
        t.fd(100)
        t.rt(90)
square(bob)

12、泛化

import turtle
bob = turtle.Turtle()
square_length = 100
n = 6
def polygon(t,length,n):
    for i in range(n):
        t.fd(length)
        t.rt(360/n)

polygon(bob,square_length,n)

给函数添加参数,就叫做泛化,这样可以让函数的功能更广泛。

13、接口设计

函数的接口就是关于它如何工作的一个概述:都有什么变量?函数实现什么功能?以及返回值是什么?允许调用者随意操作而不用处理一些无关紧要的细节,这种函数接口就是简洁的。

import turtle
import math
bob = turtle.Turtle()

def polygon(t,length,n):
    for i in range(n):
        t.fd(length)
        t.rt(360/n) 
        
def circle(t,r,n):
    circle_long = 2 * math.pi * r
    per_length = circle_long / n
    polygon(t,per_length,n)
 
# 令多边形周长尽量约等于圆周长,2Πr = n * length
# 2 * 3 * 833 ≈ 100 * 50
circle(bob,833,50)

14、重构

15、开发计划

开发计划是写程序的一系列过程。如上所用的就是『封装-泛化』的模式。这一过程的步骤如下

  1. 开始写一个特别小的程序,没有函数定义。

  2. 一旦有你的程序能用了,确定一下实现功能的这部分有练习的语句,封装成函数,并命名一下。

  3. 通过逐步给这个函数增加参数的方式来泛化。

  4. 重复1-3步骤,一直到你有了一系列能工作的函数为止。把函数复制粘贴出来,避免重复输入或者修改了。

  5. 看看是不是有通过重构来改进函数的可能。比如,假设你在一些地方看到了相似的代码,就可以把这部分代码做成一个函数。

    这个模式有一些缺点,我们后续会看到一些替代的方式,但这个模式是很有用的,尤其对实现不值得怎么去把程序分成多个函数的情况。

16、函数接口调试

一个交互接口,就像是函数和调用者的一个中间人。调用者提供特定的参数,函数完成特定的任务。

例如上面的polygon函数, t 应该是一个Turtle小乌龟、length应该是一个正数、n必须是一个整型;这些要求叫做 [ 前置条件 ],因为要在函数开始之前就要实现才行。

相应的在函数的结尾那里的条件叫 [ 后置条件 ] ,后置条件包含函数的预期效果(如画线段)和其他作用(如移动海龟)

前置条件是准备给函数调用者的。如果调用者违背了(妥当标注的)前置条件,然后函数不能正常工作,这个bug就会反馈在函数调用者上,而不是函数本身

如果前置条件得到了满足,而后置条件未能满足,这个bug就是函数的了。所以如果你的前后置条件都弄清晰,对调试很有帮助

17、floor除法

运算符使两个右斜杠 “ // ”,与传统除法不同,floor除法会把运算结果的小数位给舍弃,返回整值·。

例如,一部电影的时间长度是105分钟,如果转换为小时制,传统的除法运算如下:

>>> minutes = 105 
>>> hours = 105 / 60
1.75

如果不写有小数的时候,floor除法返回的就是整的小时数,舍弃掉小数位:

>>> minutes = 105 
>>> minutes // 60
1

若想要知道所舍弃的那一部分的大小,可以用分钟数减去这一个小时,然后剩下的分钟数就是:

>>> remainder = minutes // 60
>>> remainder
45

求模运算同样可以得到余数,运算符为 “ % ”,

>>> remainder = minutes % 60 
>>> remainder 
45

求模运算还可以用来判断按一个数能否被另一个数整除---比如 x % y 如果等于 0 了,那就是意味着能被y整除。另外,x % 10 可以得到该数的个位数字,x % 100就可以得到该数个位和百位的数字

18、布尔表达式

布尔表达式是一种非对即错的表达式,只有两个值,true(真)、false(假)。

>>> type(True) 
<class 'bool'>  
>>> type(False) 
<class 'bool'>

19、关系运算符

x==y # x is equal to y 二者相等

x != y # x is not equal to y 二者不相等

x > y # x is greater than y 前者更大

x < y # x is less than y 前者更小

x >= y # x is greater than or equal to y 大于等于

x >= y # x is greater than or equal to y 大于等于

x <= y # x is less than or equal to y 小于等于

20、逻辑运算符

逻辑运算符有三种:且、或以及非

21、if判断

条件执行:

if后面的布尔表达式就叫做条件。如果条件为真,随后缩进的语句就运行。如果条件为假,就不运行

if x > 0:
print("x is positive")

if语句与函数定义的结构基本一样:一个头部,后面跟着缩进的语句。这样的语句叫做复合语句。

复合语句中语句体内的语句数量是不限制的,但至少要有一个。有的时候会遇到一个语句体内不放语句的情况,比如空出来用来后续补充。这种情况下,你就可以用pass语句,就是啥也不会做的。

if x < 0:
pass

选择执行:

if语句的第二种形式就是『选择执行』,这种情况下会存在两种备选的语句,根据条件来判断执行哪一个

if x % 2 == 0:
print("x is even")
else:
print("x is odd")

这里条件非真即假,只有两个选择。这些选择也叫『分支』,因为在运行流程上产生了不同的分支。

链式条件:

有时候遇到的情况可能性不止两种,需要更多的分支。之时候可以用连锁条件来实现:

if x > y:
print("x is less than y")
elif x > y:
print("x is greater than y")
else:
print("x and y are equal")

elif是『else if』的缩写。这回也还是只会有一个分支的语句会被运行。elif语句的数量是无限制的。如果有else语句的话,这个else语句必须放到整个条件链的末尾,不过else语句并不是必须有的

if choice == 'a':
draw_a()
elif choice == 'b':
draw_b()
elif choice == 'c':
draw_c()

每一个条件都会依次被检查。如果第一个是假,下一个就会被检查,依此类推。如果有一个为真了,相应的分支语句就运行了,这些条件判断的语句就都结束了。如果有多个条件为真,只有先出现的为真的条件所对应的分支语句会运行

嵌套条件:

一个条件判断也可以嵌套在另一个条件判断内。

if x == y:
    print("x and y are equal")
else:
    if x < y:
        print("x is less than y")
    else:
        print("x is greater than y")

逻辑运算符有时候对简化嵌套条件判断很有用,

if x > 0:
    if x < 10:
        print("x 是个小于10的正数")

# 我们可以用逻辑运算符来实现同样的效果
if x > 0 and x < 10:
    print("x 是个小于10的正数")
    
# 或者,python提供的更简洁的方法
if 0 < x < 10:
    print("x 是个小于10的正数")

22、递归运算

一个函数可以去调用另一个函数;函数也可以来调用自己,这就是递归。

def countdown(n):
 if n < = 0:
     print("Blastoff")
 else:
     print(n)
     countdown(n-1)

如果n为0或者负数,程序会输出 “ Blastoff ”。其他情况下,程序会调用自身来运行,以自身参数n减去1为参数。

>>> countdown(3)
3
2
1
Blastoff!

调用自身的函数就是递归的;执行这种函数的过程就叫递归运算。

def print_n(s,n):
 if n <= 0:
     return
 print(s)
 print_n(s,n-1)

s = "Python is super cool"
n = 4
print_n(s,n)

如果n小于等于0了,返回语句return就会终止函数的运行。运行流程立即返回到函数调用者,函数其余各行的代码也都不会执行。

函数其余部分的代码很容易理解:print一下s,然后调用自身,用n-1做参数来继续运行,这样就额外对s进行了n-1次的显示。所以输出的行数是1+(n-1),最终一共有n行输出。

上面这种简单的例子,实际上用for循环更简单。不过有些情况下用for循环不太好写,这些情况往往用递归更简单

23、无穷递归

如果一个递归一直都不能到达基准条件,那就会持续不断地进行自我调用,程序也就永远不会终止了。这就叫无穷递归。

def recurse():
 recurse()

在大多数的开发环境下,无穷递归的程序并不会真的永远运行下去。Python会在函数达到允许递归的最大层次后返回一个错误信息:

File "<stdin>", line 2, in recurse
RuntimeError: Maximum recursion depth exceeded

这种错误出现的时候,栈中都已经有1000层递归框架了!

24、键盘输入

Python提供了内置的一个函数,名叫input,这个函数会停止程序运行,等待用户来输入一些内容。用户按下 ESC键 或 Enter回车键。程序就恢复运行,input函数就把用户输入的内容作为字符串返回。

>>> text = input()
What are you 弄啥嘞?
>>> text
'What are you 弄啥嘞?'

在用户输入内容之前,最好显示一些提示来告诉用户需要输入什么内容。input函数能够把提示内容作为参数:

>>> name = input("What is your name?\n")
What is your name?
ishells
>>> name 
'ishells'

用户从键盘输入的类型默认是“ str ”,如果想得到其他类型的变量,需要手动转换一下“:

>>> text = "你一般跑步时长是多少分钟?"
>>> time = input(text)
"你一般跑步时长是多少分钟?"
42
>>> time
42 
>>> type(time)
<class 'str'>
>>> int(time)
42

这个时候,如果调皮的用户输入的不是一串数字,而是其他内容,使用int()进行类型转换就会得到一个错误:

>>> time = input(text)
"你一般跑步时长是多少分钟?"
42minutes
>>> int(minutes)
ValueError: invalid literal for int() with base 10

25、调试

当语法错误或者运行错误出现的时候,错误信息会包含很多有用的信息,不过信息量太大,太繁杂。最有用的也就下面这两类:

错误的类型是什么

错误的位置在哪里

>>> x = 5
>>>  y = 6
File "<stdin>", line 1     
	y = 6      
    ^
IndentationError: unexpected inden

这个例子里面,错误的地方是第二行开头用一个空格来缩进了。但这个错误是指向y的,这就有点误导了。一般情况下,错误信息都会表示出发现问题的位置,但具体的错误可能是在此位置之前的代码引起的,有的时候甚至是前一行

import math 
signal_power = 9
noiser_power = 10
ratio = signal_power // noise_power
decibels = 10 * math.log10(ratio)
print(decibels)

# 运行上面的程序就会得到如下错误信息:
Traceback (most recent call last):    
    File "snr.py", line 5, in ?        
    	decibels = 10 * math.log10(ratio)
ValueError: math domain err

这个错误信息提示第五行,但那一行实际上并没有错。要找到真正的错误,就要输出一下ratio的值来看一下,结果发现是0了。那问题实际是在第四行,应该用浮点除法,结果多打了一个右斜杠,弄成了地板除法,才导致的错误。

所以得花点时间仔细阅读错误信息,但不要轻易就认为出错信息说的内容都是完全正确可靠的

26、有返回值的函数

函数使用return返回的值是局部变量,函数外部是不能直接通过该局部变量获取到函数返回的结果的,是需要定义一个值来获取函数返回的结果

import math
def area(radius):
 a = math.pi * radius ** 2
 return a

# 在有返回值的函数中,返回语句可以包含表达式,返回语句的意思是:立即返回下面的表达式作为返回值。返回语句里面的表达式可以随便多复杂都行。
def area(radius):
 return math.pi * radius**2

area(2)

# 上面的例子中,a是一个局部变量,并不能在函数外部使用,如果要获取到函数内部的a的值,就要在函数外部定义一个变量获取该值。

另外,有一些临时变量可以让后续的调试过程更简单。所以有时候可以多设置几条返回语句,每一条都对应一种情况

def absolute_value(x):
 if x < 0:
     return -x 
 else:
     return x

# 因为这些返回语句对应的是不同条件,因此实际上最终只会有一个返回动作执行

返回语句运行的时候,函数就结束了,也不会运行任何其他的语句了。返回语句后面的代码,执行流程里所有其他的位置都无法再触碰了,这种代码叫做『死亡代码』

def absolute_value(x):
 if x < 0:
     return -x
 if x > 0:
     return x
 
# 这个函数就是错误的,因为一旦x等于0了,两个条件都没满足,没有触发返回语句,函数就结束了。执行流程走完这个函数之后,返回的就是空(None),而本应该返回0的绝对值的

顺便说一下,Python内置函数就有一个叫abs的,就是用来求绝对值的,abs(-10)

27、增量式开发

写一些复杂函数的时候,会发现要花很多时间调试。

要应对越来越复杂的程序,其实不妨来试试增量式开发的办法。**增量式开发的目的是避免长时间的调试过程,一点点对已有的小规模代码进行增补和测试。**比如写一个函数的时候,先确保语法正确,在尝试一步一步的完善函数体,逐步完成要实现的功能

28、更多递归

def factorial(n):
 if n == 0:
     return 1
 else:        
     recurse = factorial(n-1)        
     result = n * recurse
 return result
# 以3为参数来调用一下这个阶乘函数:
'''
3不是0,所以分支一下,计算n-1的阶乘。。。
2不是0,所以分支一下,计算n-1的阶乘。。。
1不是0,所以分支一下,计算n-1的阶乘。。。

第一遍:
recurse = factorial(3-1)
result = 3 * factorial(3-1)
第二遍:
recurse = factorial(2-1)
result = 2 * factorial(2-1)
第三遍:
recurse = factorial(1-1)
result = 1 * factorial(1-1)
第四遍:
返回1
然后return返回:
result = 3 * 2 * 1 * 1
'''

跟随着运行流程是阅读程序的一种方法,但很快就容易弄糊涂。**当你遇到一个函数调用的时候,你不用去追踪具体的执行流程,而是假设这个函数工作正常并且返回正确的结果。**其实当使用内置函数的时候,就已经使用过这种方法了。比如当你调用math.cos的时候,你并没有仔细查看这些函数的函数体,而是直接假设它们都工作,直接使用了。

对于递归函数而言也是同样道理。当你进行递归调用的时候,并不用追踪执行流程,你只需要假设递归调用正常工作,返回正确结果

Tip:

isinstance(obj,class_or_tuple,/) 
# 内置函数,可以判断对象的类型
# 例:
>>> isinstance(1.5,int)
False
>>> isinstance(1.5.float)
True