首页 > 技术文章 > Python入门学习(一)

wwf828 2017-11-06 16:29 原文

看完了莫烦Python的视频,对于Python有了一点感觉,接下来打算把小甲鱼的视频啃完,附上学习网址:http://blog.fishc.com/category/python

小甲鱼的视频是从零基础开始的,所以这篇笔记补充一些注意点和新知识点,有很多内容在前面的笔记中已经提及:《Python初学基础,在这里就不再赘述啦。

1 Python & IDLE

Python 是什么类型的语言?

Python是脚本语言

脚本语言(Scripting language)是电脑编程语言,因此也能让开发者藉以编写出让电脑听命行事的程序。以简单的方式快速完成某些复杂的事情通常是创造脚本语言的重要原则,基于这项原则,使得脚本语言通常比 C语言、C++语言 Java 之类的系统编程语言要简单容易。

也让脚本语言另有一些属于脚本语言的特性:

  • 语法和结构通常比较简单
  • 学习和使用通常比较简单
  • 通常以容易修改程序的解释作为运行方式,而不需要编译
  • 程序的开发产能优于运行性能


一个脚本可以使得本来要用键盘进行的相互式操作自动化。一个Shell脚本主要由原本需要在命令行输入的命令组成,或在一个文本编辑器中,用户可以使用脚本来把一些常用的操作组合成一组串行。主要用来书写这种脚本的语言叫做脚本语言。很多脚本语言实际上已经超过简单的用户命令串行的指令,还可以编写更复杂的程序。

IDLE是什么?

IDLE是一个Python Shell,shell的意思就是“外壳”,基本上来说,就是一个通过键入文本与程序交互的途径!像我们Windows那个cmd窗口,像Linux那个黑乎乎的命令窗口,他们都是shell,利用他们,我们就可以给操作系统下达命令。同样的,我们可以利用IDLE这个shell与Python进行互动。

2 BIF

BIF就是Built-in Functions,内置函数。为了方便程序员快速编写脚本程序,Python提供了非常丰富的内置函数,我们只需要直接调用即可,不是内置函数则需通过import语句调入扩展包后才能使用。在IDLE下键入dir(__builtins__)即可查看所有的内置函数,help(内置函数名)则可产看函数的使用方法。

3 变量和字符串

变量:在Python中变量不需要事先声明,但是需要先赋值后再使用,变量更像是贴在值上的标签,这给Python带来了很大便捷。变量的名字和C及java编程语言一样,由字母、数字、下划线组成,且首位不允许是数字,而且变量名最好能专业些,让人一看就知道其含义。

字符串:用单引号或者双引号均可,如果一个字符串本身又包含字符串,可使用转义字符'\',对于想要将其作为一个字符处理的单引号或者双引号而言,前面只要加上转义字符就可以了。

例如打印Let's go! 下述两种方法均可

print("Let's go!")
print('Let\'s go!')

原始字符串:对于频繁使用到反斜杠的特殊字符串,一个一个去添加转义字符显得繁琐,可在字符串前面加上一个‘r’,这就是原始字符串的含义。例如字符串C:\Program File\Intel\WiFi\Help,定义只要r'C:\Program File\Intel\WiFi\Help',它的实现方法其实很简单,就是程序会自动对每一个反斜杠前面再加一个反斜杠。但是r不能对末尾是反斜杠的字符串进行定义,'C:\Temp\'。这个时候如果非要使用r进行原始字符串操作的话可以采用如下形式解决:r'C:\Temp' '\\' 或者r'C:\Temp'+'\\'。

对于多行的较长的字符串,可以通过三引号,单引号或者双引号均可。

与列表和元组一样,字符串也可以进行分片:

字符串和元组一样,里面的元素是不能进行修改和删除的。

字符串内置函数用法可参考:《字符串的方法及注释

字符串的format函数,通过花括号{}来表明字符串中需要格式化的值,{}内的值可以是位置参数(0,1,2,3等)或关键字参数

例如:

使用关键字参数时必须赋值,关键字和位置参数可以混用,但是位置参数应放在左边。

在字符串中花括号表示的内容是要被替换的,如果想让花括号和其中的内容作为一个正常字符串处理的话,必须在外面再加上一层花括号,原理和转义字符前加上转义字符一样,例:

位置参数直接跟‘:’表明后面要跟一个字符串格式化操作符,例:

字符串格式化操作符可参考:《字符串格式化符号含义及转义字符含义

4 猜数字小游戏

之前第二讲所设计的文字小游戏如下所示,此节对该代码进行改进增加功能:

  • 猜错的时候给提示,例如大了还是小了(条件分支)
  • 每运行一次程序只能猜一次,应提供多次机会给用户猜测(while循环)
  • 每次运行程序,答案可以是随机的(random模块)

input是内置函数,返回的是字符串类型,用于if判断时类型强制转换为int型

print('----------------游戏开始---------------')
temp = input('猜猜看我现在心里想的是哪个数字:')
guess = int(temp)
if guess == 6:
    print('猜对啦!')
    print('但是没有奖励哈哈哈')
else:
    print('猜错啦,我心里想的是6')
print('游戏结束,不玩啦')

首先完成第一个功能,添加if条件分支:

print('----------------游戏开始---------------')
temp = input('猜猜看我现在心里想的是哪个数字:')
guess = int(temp)
if guess == 6:
    print('猜对啦!')
    print('但是没有奖励哈哈哈')
elif guess > 6:
    print('比我心里想的数字大!')
else:
    print('比我心里想的数字小!')
print('游戏结束,不玩啦')

第二步通过while循环实现未猜中时继续游戏:

temp = input('猜猜看我现在心里想的是哪个数字:')
guess = int(temp)
print('----------------游戏开始---------------')
while guess != 6:
    temp = input('猜错了,请重新输入吧:')
    guess = int(temp)
    if guess == 6:
        print('猜对啦!')
        print('但是没有奖励哈哈哈')
    if guess > 6:
        print('比我心里想的数字大!')
    else:
        print('比我心里想的数字小!')
print('游戏结束,不玩啦')

此外,还要求游戏机会只有三次,最后一个功能import模块random,通过其中的randint来产生随机数,代码如下所示:

import random
secret = random.randint(1,10)
print('----------------游戏开始---------------')
times = 3
temp = input('猜猜看我现在心里想的是哪个数字,注意只有三次机会哦:')
guess = int(temp)
times -= 1
if guess == secret:
    print('猜对啦!')
    print('但是没有奖励哈哈哈')
else:
    while (guess!=secret) and (times > 0):
        if guess > secret:
            print('比我心里想的数字大!')
        else:
            print('比我心里想的数字小!')
        temp = input('猜错了,请重新输入吧:')
        guess = int(temp)
        times -= 1
        if guess == secret:
            print('恭喜,猜对啦!')
        if times == 0:
            print('三次机会已用光')
print('游戏结束,不玩啦')

或者:

import random
secret = random.randint(1,10)
times = 3
print('----------------游戏开始---------------')
guess = 0
print('猜猜看我现在心里想的是哪个数字,注意只有三次机会哦:',end=' ')
while (guess!=secret) and (times > 0):
    temp = input()
    guess = int(temp)
    times -= 1
    if guess == secret:
        print('猜对啦!')
        print('但是没有奖励哈哈哈')
    elif guess > secret:
        print('比我心里想的数字大!')
    else:
        print('比我心里想的数字小!')
    if times > 0:
        print('再试一次吧:',end=' ')
    else:
        print('三次机会已用光')
        
print('游戏结束,不玩啦')
Python的 and 操作符 和C语言的 && 操作符 有何不同?
在c中,0&&3 = 0,1&&3 = 1在python中,,0 and 3 = 0,1 and 3 = 3,3 and 4 = 4,3 or 4 = 3
例x and y,Python 的做法是如果 x 为假,表达式会返回 x 的值(0),否则它就会返回 y 的值
逻辑操作符有个有趣的特性:在不需要求值的时候不进行操作。这么说可能比较“高深”,举个例子,表达式 x and y,需要 x 和 y 两个变量同时为真(True)的时候,结果才为真。因此,如果当 x 变量得知是假(False)的时候,表达式就会立刻返回 False,而不用去管 y 变量的值。
这种行为被称为短路逻辑(short-circuit logic)或者惰性求值(lazy evaluation),这种行为同样也应用与 or 操作符。

5 数据类型

5.1 基本数据类型

(1)整型,在Python3中长整形和整形归为一类,所有的整数都属于整型,例如1,0,1000,1203等等
(2)浮点型,数字中有小数点的数,如12.1   1.85   10.0 
 等等,另外1e10也表示浮点数
(3)字符串,所有以单引号或双引号括起来的值都叫做字符串
(4)布尔类型, 布尔类型只有两个值,True 或False ,记得在Python里面这两个值首字母均大写。

5.2 数据类型转换

整型强制转换函数 int(),可以将纯数字的字符串或浮点型强制转换为整形,如int('0012')为12;  int(12.5)为12
字符串强制类型转换函数str(),可以将任何类型转换为字符串类型,例如str(1e28)为‘1e+28'  str(1e2)为'100.0'等等
浮点型强制转换函数float(),可以将包含一个小数点的数字字符串或者整形转换为浮点型,如float('0012')为12.0,float('.123')为0.123

5.3 数据类型信息获取

函数type(),直接给出数的数据类型,例type(False)返回bool,函数isinstance()需要给出需要判断的数和一个数据类型,是则返回True,否则返回False,如isinstace(1,int)返回True,isinstance('I love u',float)返回False,而isinstance('I love u',str)返回True

补充字符串的一些功能:

s为字符串
s.isalnum()  所有字符都是数字或者字母,为真返回 Ture,否则返回 False。
s.isalpha()   所有字符都是字母,为真返回 Ture,否则返回 False。
s.isdigit()     所有字符都是数字,为真返回 Ture,否则返回 False。
s.islower()   所有字符都是小写,为真返回 Ture,否则返回 False。
s.isupper()  所有字符都是大写,为真返回 Ture,否则返回 False。
s.istitle()      所有单词都是首字母大写,为真返回 Ture,否则返回 False。
s.isspace()  所有字符都是空白字符,为真返回 Ture,否则返回 False。

6 常用操作符

算数运算符: 加(+),减(-),乘(*),除(/),幂运算(**),地板除(//)

/和//的区别
在Python中的除运算符与其它程序语言的不太一样,/表示真正的除号,例如1/3=0.3333333333333333,而4/2的值为2.0。说明两个数相除的值是一个浮点数,而其它程序语言/却表示两个整数相除,只返回一个整数。Python后来为了兼容这种用法,引入了//来实现。例如3//2的值为1,而3.0//2的值为1.0,且3//2.0的值也为1.0。说明在Python中//符号两边同为整数时的值才为整数,否则则为一个浮点数(后面带'.0')。

逻辑运算符:and or not,优先级为not>and>or

比较运算符:<  <=  >  >=  ==  !=

7 分支和循环

三目运算符:形式为 x if 条件表达式 else y,先判断条件表达式真假,真则取x的值,否则取y的值。举个例子 3 if 4>3 else 4  值为3,而3 if 4<3 else 4 的值为4 

断言语句assertassert后面跟一个条件表达式,如果条件表达式的值为假是程序自动崩溃并报异常AssertionError,如果为真则继续执行后面的语句,主要用于在程序中植入检查点,只有assert后面的条件永远为真时程序才能正常运行,否则就崩溃。例如 assert 3>4,程序就会执行这一条语句后崩溃并报出异常,而assert 4>3则正常执行

8 列表

Python列表的含义和其他程序语言中的数组类似,由一系列元素组成并且可以通过下标进行访问,不同的是,列表里面的元素可以是任何数据类型,甚至可以是一个列表。

在列表内预先定义好的函数叫方法,下面介绍三种插入新元素的方法:append(),extent(),insert(),需要列表名加成员操作符.以及方法名来使用

8.1 插入新元素

(1)append()函数

一次添加一个元素到列表末尾,调用方式为列表.append(新元素)

(2)extend()函数

一次添加多个元素到列表末尾,括号内的参数是列表,调用方式为列表.extend(要加入的列表)

(3)insert()函数

可将元素加入到列表中的特定位置,注意列表元素的位置是从0开始的,调用方式为列表.insert(位置,元素)

8.2 从列表中删除元素

(1)remove()

参数是一个元素,列表.remove(元素),如果元素不在列表中,将报错

(2)del

直接删除一个元素值,可以直接通过索引值获得需要删除的元素,del 列表名[索引值]

如果执行del 列表名,则列表将被删除

(3)pop()

不给出参数则直接返回列表中最后一个元素并从列表中删除

a = [1,2,3,4,1,1,-1]
b = a.pop()

执行上述语句后,b = -1,a = [1,2,3,4,1,1]

也可以指定索引值,直接返回索引值对应元素并从列表中删除

8.3 列表分片

当需要从列表一次性取出多个元素时,需要通过列表分片的方式来实现,基本形式是列表名[左索引值:右索引值:步长],(指定右索引值时不包括该元素)左右索引值及步长都可以忽略,左索引值忽略时表明列表元素从0开始,右索引值省略表示包括索引值右边的所有元素,步长省略默认为1。

a[1:3],输出[2, 3]

a[:3],输出[1, 2, 3]

a[1:],输出[2, 3, 4, 1, 1, -1]

a[:]得到列表的拷贝

8.4 列表拷贝

拷贝方法:

(1)已知列表名直接赋值给一个变量

(2)通过分片操作拷贝

(3)通过列表内置方法copy()赋值

(1)两个变量指向同一个数据存储空间,存储空间内数值变化,则两者均改变,(2)(3)相当于重新建立了一个存储空间,里面的数据不受其他空间内数值变化影响。

8.5 列表中的常用操作符

1)比较运算符

列表可以直接比较大小,从第0个元素开始比较

2)逻辑运算符

3)拼接运算符

+不能进行新元素的操作,两边的必须都是列表

4)重复

5)成员关系操作符

in和not in

8.6 内置方法

着重介绍reverse()和sort()方法

reverse将所有元素倒置

L.sort(key=None,reverse=False)默认是从小到大排序,若想从大到小,则将reverse的值改为True

8.7 列表推导式/列表解析简介

列表推导式(list comprehensions)也叫列表解析,灵感取自于函数式变成语言Haskell,可以用来动态创建列表,一般形式如下:

[有关A的表达式 for A in B],例:

9 元组

9.1 创建和访问元组

元组和列表类似,但是元组一旦创建,元组内的元素不允许修改和删除,这是元组和列表最大的区别。元组的标示是()

当元组中仅有一个元素时,需要在元素后面加上逗号,例:

只有加上逗号时,类型才是一个tuple,甚至不需要括号,仅仅有逗号就行,即t=1,再如:

元组的访问和列表一样,都是元组名[索引值]

9.2 更新和删除元组

虽然元组本身不允许修改和删除,但是和字符串一样,可以对原元组的元素进行操作并生成一个新的元组,例:

通过分片和拼接,其实是生成了一个新的元组,赋值给了t,原来的元组还存在,但是不再叫t了,将会被Python内存垃圾回收。

可以通过del语句直接删除一个元组,但是不允许利用del删除元组的一个元素

但同样可以通过分片和拼接来实现某个元素的删除:

同样,原来的元组还存在,但是不再叫t了,将会被Python内存垃圾回收。

10 函数

函数的定义和参数的使用可参考《Python初学基础》,这里补充一些新的知识点。

10.1 全局变量

在python中可以整个代码内访问全局变量,但是不要试图在函数内部去修改它。如果在函数内修改全局变量的值,Python会使用屏蔽(Shadowing)的方式保护全局变量,将会在函数内部自动创建一个新的局部变量,名字和全局变量相同。二者互不影响,因为存储空间是不一样的。这样在函数内部所修改的其实是局部变量,而不会影响到全局变量。

举例说明:

def discounts(price,rate):
    final_price = price * rate
    old_price = 50
    print('修改后old_price的值1:',old_price)
    return final_price

old_price = float(input('请输入原价:'))
rate = float(input('请输入折扣率:'))
new_price = discounts(old_price,rate)
print('修改后old_price的值2:',old_price)
print('打折后的价格是:',new_price)

运行结果:

在函数内访问全局变量old_price,并试图去修改它,可发现输出的值1已经改变,而在函数外打印的值2依然没有改变,仍然等于调用函数时所赋的100。在函数体内的操作并没有改变全局变量的值。

如果一定要在函数内部对全局变量进行修改,可在函数内部先用global关键字声明该变量为全局变量。

10.2 闭包

Python中的闭包从表现形式上定义为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

闭包是一种满足特定要求的内嵌函数,这里讲内嵌函数称为子函数,其外部函数称为母函数,则当子函数体内有对母函数体内定义的变量的引用时我们称这个子函数为一个闭包。但当母函数和其闭包都定义了参数时,由于在母函数体外是无法直接对闭包进行函数调用的,为了能够实现对闭包的调用,需要在母函数内增加一条返回闭包函数名本身的语句,这个时候调用母函数后返回的是一个闭包的函数对象,就可以通过这种方法间接调用闭包函数了。例:

上述函数还可以使用funx(5)(8)进行调用。

无法直接在闭包内部对外部函数的变量进行修改,但是如果非要修改的话,在Python3中是可以的,需要增加一条声明变量是外部函数内变量的语句nonlocal

如上所示,若在fun2中直接调用fun1的x会报错,加上nonlocal后可以正常运行。

10.3 lambda表达式

lambda关键字用来创建匿名函数(隐函数),定义形式:形参:返回值表达式,相对于一般函数而言具有以下优势:
(1)免去了函数定义过程,代码变得更加精简

(2)省却定义函数名过程

(3)省去了返回到函数定义出阅读代码过程,提高了代码的可读性

如下所以,lambda表达式也可对多个形参进行处理

10.4 两个内置函数filter()和map()

(1)filter()

filter()函数实现过滤功能,有两个参数,第一个参数为None或一个函数对象,第二个参数为一个可迭代的对象,当第一个参数为None时,实现将可迭代对象的每一个元素值为False的过滤掉,生成一个仅包含元素值为True的新可迭代对象

如下所示,将值为False过滤,留下值为True的值

还可以通过自定义function来实现不同的功能,如下所示为输出0-9的奇数

也可通过lambda表达式来直接定义

(2)map()

map()函数与filter函数形式一致,实现对可迭代对象的映射,但其第一个参数必须是一个函数对象,而且map()函数是将经过函数对象处理后的返回值作为新的元素组成一个可迭代对象的,举例说明:

11 递归

递归就是函数通过return语句实现自己调用自己的过程。

Python3中针对递归提供了程序保护机制,默认允许的递归深度是100层,而如果我们使用网络爬虫等需要远远超过百次的递归层次时,就需要去修改程序默认的递归深度以满足要求。设置递归深度的函数在sys模块里面的settrecursionlimit()函数进行设置,如将递归层次设置为110层:

import sys
sys.setrecursionlimit(110)

11.1 阶乘

如下为不采用递归的方式来实现一个数的阶乘

def factorial(n):
    result = n
    for i in range(1,n):
        result *= i
    return result

number = int(input('请输入一个正整数:'))
result = factorial(number)
print('%d的阶乘是:%d'%(number,result))

采用递归方式如下:

def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n-1)

number = int(input('请输入一个正整数:'))
result = factorial(number)
print('%d的阶乘是:%d'%(number,result))

大多数问题使用递归实现起来更高效,在某些特殊场合合理的使用会使得你的代码精简且高效,但是递归是函数调用自身,调用函数需要入栈出栈,对于内存和CPU的消耗还是比较大的,对于上述求阶乘的例子,当输入一个比较大的数的时候采用递归消耗比较大,反而迭代会比递归来得更有效率。

11.2 斐波那契(Fibonacci)数列

如果说兔子在出生两个月后就有繁殖能力,在拥有繁殖能力后,这对兔子每个月能生出一对小兔子,假设所有的兔子都不会死去,那么一年之后可以繁殖多少对兔子呢?

小兔子每月的总对数呈Fibonacci数列形式:

月份 1 2 3 4 5 6 7 8 9 10 11 12
兔子总对数 1 1 2 3 5 8 13 21 34 55 89 144

 

 

将兔子总对数与月份n的关系归纳为数学公式则为

F(n) = 1                           n=1

     1                            n=2

          F(n-1)+F(n-2)        n>2

则接着我们采用迭代和递归的方式来计算20个月后兔子的总对数

(1)迭代方式:

def fab(n):
    n1 = 1
    n2 = 1
    n3 = 0
    if n < 1:
        print('输入有误!')
        return -1
    while(n-2) > 0:
        n3 = n1 + n2
        n1 = n2
        n2 = n3
        n -= 1
    return n3

number = int(input('请输入月份:'))
sum = fab(number)
if sum != -1:
    print('%d个月后总共有%d对小兔子'%(number,sum))

(2)递归方式:

def fab(n):
    sum = 0
    if n < 1:
        print('输入有误!')
        return -1
    if n == 1 or n == 2:
        sum = 1
    else:
        sum = fab(n-1)+fab(n-2)
    return sum

number = int(input('请输入月份:'))
sum = fab(number)
if sum != -1:
  print('%d个月后总共有%d对小兔子'%(number,sum))

当月份数字比较小的时候,两者运行的速度都比较快,但是当月份数目比较大的时候,递归的效率明显变慢,运行结果如下:

递归是一个双刃剑,用好了能解决很棘手的问题,但是用不好会使得程序的执行效率降低。

11.3 汉诺塔

汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。递归算法的思想是“分制”方法,即将一个复杂问题分解为几个较复杂问题,再继续将较负责问题分解为次较复杂问题,继续将次较复杂问题往下分解直到分解为一个能直接解决的简单问题。采用递归方法解决汉诺塔游戏的思路如下:将n个盘子从x柱子转移到z柱子问题分解为三个问题:

(1)将x柱子顶上的n-1个盘子借助z转移到y柱子上

(2)将x柱子最后一个盘子转移到z柱子上

(3)将y柱子上的n-1个盘子借助x转移到z柱子上

依次将问题分解下去,以64为例

(1)可以继续分解为:
11)将前62个盘子从x移动到z上

12)将最底下的第63个盘子移动到y上

13)将z上的62个盘子移动到y上同理(3)也可以继续分解,代码如下所示:

def hanoi(n,x,y,z):                #n代表盘子数量,x,y,z代表相应柱子
    global count                   #声明为全局变量,统计一共要移动盘子的次数
    count += 1
    if n == 1:
        print('%c -> %c'%(x,z))
    else:
        hanoi(n-1,x,z,y)           #将n-1个盘子从x借助z移动到y
        print('%c -> %c'%(x,z))    #将最后一个盘子从x移动到z
        hanoi(n-1,y,x,z)           #将n-1个盘子从y借助x移动到z
count = 0
number = int(input('请输入盘子总数:'))
hanoi(number,'x','y','z')
print('一共需要移动盘子的次数为%d次!'%count)

如下所示为自己绘制的以3为例的程序流程:

 

 

 

推荐阅读