【Python】:Python概览

【Python】Python介绍

标签(空格分隔):【Python】


文章目录

    • 1.1 简史
    • 1.2 不同之处
    • 1.3 数字
    • 1.4 第一个程序
    • 2.1 决策和循环
    • 3.1 列表
    • 3.2 列表推导和枚举
    • 4.1 字符串
    • 4.2 操作字符
    • 5.1 高级函数技术
    • 6.1 局部变量和全局变量
    • 7.1 文件操作
    • 8.1 字典和集合
    • 9.1 矩阵:二维列表
    • 10.1 类和对象
    • 11.1 Python 高级技术
    • 我的微信公众号

1.1 简史

Python 衍生自 ABC语言。如今主要有两大版本:2.x与 3.x. 本文主要针对 3.x.

1.2 不同之处

  • Python 是免费的,且很多扩展包是免费的。
  • Python 不提供代码块的其实和结束语法,代码之间的各种关系都是使用缩进表示的。
  • Python 没有变量声明的概念,变量都是通过它们赋值来创造的。但这存在隐藏极深的陷阱。
  • Python 大量使用了迭代的概念,迭代即遍历序列。

总而言之,Python 通常被视为“原型设计”或“应用程序快速开发”的语言。可以用Python快速编写出程序,若需要效率,再使用C, C++改写。

1.3 数字

Python 内置的数字功能十分强大, 它支持复数、浮点数、分数和“无穷大整数”。
启动IDLE后,可以写出一下例子:

# 输入随意的数字,例如1,会输出该数字
>>>1
1
 
# IDLE是一个方便的计算器,例如:
>>>1 + 2 * (3 / 4) ** 5
1.474609375
# 其中 "**"代表指数

# Python 可以处理非常大的数字,且整数绝对精确,例如:
>>>9 ** 20
12157665459056928801

# 同时,Python 还支持布尔类型
>>>10**40 + 1 == 10**40
False

# Python 还支持浮点数,例如除法:
>>>15/2
7.5
>>>15//2
7

# 当然还支持求余:
>>>15%2
1

# 在Python中,不同于整数,浮点数是存在精度的,例如:
>>>9**30    #整数
42391158275216203514294433201
>>>9.0**30  #浮点数
4.23911582752162e+28
#此时由于精度问题,有:
>>>10.0**40 + 1 == 10.0**40
True
  • 将数字赋给变量
    变量的命名规则如下:1. 变量名的第一个字符必须是下划线或字母;2. 其他字符可以是下划线、字母、数字。
    例如:
>>> a = 1
>>> b = 2
>>> a + b   #使用变量前必须创建变量
3

# Python中,变量名区分大小写。例如:
>>>a
1
>>>A = 3
>>>A
3

# 注意,Python中将每个变量都视为引用!
# 此时,使用type()进行辅助:
>>>a = 1
>>>b = 1
>>>type(a)
<class 'int'>
>>>type(b)
<class 'int'>
>>>b = 2.1
>>>type(b)
<class 'float'>
# 起初,a, b被赋值为1,同时指向整数1,为int类型,随后b指向2.1 变为float类型。这就是引用机制的后果。

# 多赋值
>>>a = b = c = d = e = 1 #a,b,c,d,e均被赋值为1.

# 元组赋值
>>>i, j, k = 100, 101, 102 #i,j,k分别是100, 101, 102;
>>>i,j,k
(100, 101, 102)
#甚至可以进行如下操作:
>>>a, b = a + b, a  #把a的新值设为 a + b, b的新值设为a的旧值

# Python也支持 +=, -+, *=, /=, **=, %= 这些复合操作

# Python还支持单目运算符, 例如:
>>>n = 3
>>>-n
-3

1.4 第一个程序

如何编写函数?假设我们需要使用一个名为convert()的函数用来将摄氏度转化为华氏温度,在Python中该如何定义函数?

#首先可以使用内置函数,但遗憾的是,python中并没有内置温度转换的函数,因此需要自己编写, 请看以下格式:

def convert( fahr):         #fahr是摄氏度; 
    cels = fahr * 1.8 + 32  #二者转化的公式
    return cels             #ceks是华氏度
#定义完成后,即可使用
>>>convert
<function convert at 0x101d63378>   #该输出表明,已经成功将convert关联到一个函数,且不存在语法错误,但运行时是否出现错误依旧未知
>>>convert(32)  #正确执行函数需要指定参数
89.6

此时,我们再回顾函数,学习其中的规则

def convert( fahr): #函数名后面是括号和参数名;第一行末尾必须输入一个冒号。
    cels = fahr * 1.8 + 32  
    return cels             #return 决定函数的返回值
#交互式环境应该使用自动缩进后续的代码行,否则可能导致错误。

Python 中如何使用缩进:在语法方面,Python与C语言家族有天壤之别。尤其是缩进方面。
交互式环境中,Python会自动缩进控制结构中的语句,我们应该接受自动缩进。
编写Python脚本文件时,想要缩进多少个空格均可以,但需要保持一致。理论上应该遵守PEB-8标准,每条语句缩进4个空格。
注意,是空格,不是制表符,因为制表符看起来像4个空格,但实际上只有一个字符。十分容易导致混用制表符和空格,进而引发语法错误。


如何打印消息?在Python中提供了内置函数print, 例如:

# 单引号与双引号区别
>>>print( 'I\'m student')   #单引号中转义字符需要加'\'
I'm student
>>>print( "I'm student")    #双引号不用考虑
I'm student

# 同时打印文本和变量
>>>x = 1
>>>print( "x = ", x) #注意,print会在相邻项之间插入一个空格,最后内容后加一个换行符。
x =  1
>>>print( "x =",x)  #注意二者区别
x = 1

如何获取输入?在Python中提供了强大的内置函数input, 例如:

#获取字符串输入
def main():
    name1_str = input("您的姓名是:")
    name2_str = input("您的年龄是:")
    print("姓名:", name1_str, "年龄:",name2_str)

>>>main()
您的姓名是:张
您的年龄是:11
姓名: 张 年龄: 11

# 上例说明 input函数可以提示用户输入一个字符串并将其赋值给一个变量

#获取数值输入,分别以整数和浮点数为例:
def main():
    in_str = input("Enter a number:")
    n = int(in_str)
    print("n + 1 =", n+1)

>>>main()
Enter a number:33
n + 1 = 34

def main():
    n = float(input("Enter a number:"))
    print("n /2 =", n/2)

>>>main()
Enter a number:7
n /2 = 3.5

#设置输出格式,Python提供了对输出字符串的格式进行打印的方法:字符'{','}'用来表示打印的字段,format来设置参数值。例如:
>>>test = "The number is {} and {}."
>>>test.format(11,22)
'The number is 11 and 22.'

2.1 决策和循环

在Python中,决策功能主要依靠控制结构,例如:

# 判断一个数是否为偶数
def is_even(n):
    remainder = n % 2;
    if remainder == 0:  #注意添加冒号':'
        print(n,"is even")
    else:               #注意添加冒号':'
        print(n,"is odd")

>>>is_even(2)
2 is even
>>>is_even(3)
3 is odd

这里给出Python中的比较晕算法和布尔运算符:

运算符 示例
== n == 5
>= n >= 5
<= n <= 5
> n > 5
< n < 5
!= n != 5
and n > 5 and n < 10
or n < 5 or n > 10
not not(n < 5 or n > 10)

另外,不同于其他语言,在Python中,还存在elif语句:

def range(n):
    if n < 10 and n > 0:
        print(n, "is below range 10 and bigger 0");
    elif n < 100 and n > 10:
        print(n, "is inside the range 10 to 100");
    elif n >= 100 or n <= 0:
        print(n, "it's too big or small")

>>>range(11)
11 is inside the range 1 to 100

循环式是编程中最重要的概念之一。
在Python中,编写循环模块最简单的方式是使用while语句,例如:

>>> n = 1
>>> while n < 10:   #不要忘记添加冒号
>>>     print(n, end = ' ') # end=' '意思是在末尾只打印一个空格
>>>     n+=1        #等价于n = n + 1;
1 2 3 4 5 6 7 8 9 

# 如何中途推出寻款?Python提供了关键字break
# 例如,猜字游戏:
import random               #导入random库中的函数
n = random.randint(1,50)    #使用随机函数
while True:                 #while True 可以建立一个死循环
    ans = int( input("Enter your guess:"))
    if ans == n:
        print("It's right")
        break;
    elif ans > n:
        print("Too big")
    else:
        print("Too small")

#某一次测试结果        
Enter your guess:25
Too small
Enter your guess:35
Too big
Enter your guess:33
Too big
Enter your guess:28
Too small
Enter your guess:30
It's right

3.1 列表

可迭代对象:一个数据源,类似于循环,可以从中不断获得下一个值,直到结尾。
列表和字符串是 Python 中最重要的可迭代对象。

# 创建一个列表
>>>test_list = [1,2,3,4,5]
# 对列表进行扩展的两种方法:
>>>test_list.append(6)
>>>test_list = test_list + [7]  #注意,这种方法效率低,因为破坏了原本的数据关联。
>>>test_list = test_list + 8 #ERROR, 不能执行
>>>test_list    #查看当前test列表
[1, 2, 3, 4, 5, 6, 7]

Python 中列表是可变的,例如:

>>>x = [1,2,3]
>>>y = [1,2,4]
>>>x == y
False
>>>y[2] = 3
>>>x == y
True

Python 中列表可以包含任何数据类型,包括其他列表,但通常是字符串或数字,甚至混合类型,例如:

>>>test = ["test", 101]
>>>test
['test', 101]

# 当列表元素类型均相同时,可以排序
>>>test2 = [321,15243,3215,4326,23,431,23,124,6,4576,35]
>>>test2.sort()
>>>test2
[6, 23, 23, 35, 124, 321, 431, 3215, 4326, 4576, 15243]

# 也可以创建一个别名,注意,这里不是复制/赋值,而是类似于指针,同时指向test指向的列表
>>>test3 = test
>>>test3
['test', 101]

# 若需要创建独立的数据副本,即两个相同的列表,可以这么做:
>>>test4 = [4,3,2,1]
>>>test5 = test4[:]     #复制元素
>>>test5.sort()
>>>test4
[4, 3, 2, 1]
>>>test5
[1, 2, 3, 4]

如何处理列表中的每一个元素?Python提供了关键字for,例如:

# 依次输出列表元素的十倍:
>>>test = [4,3,2,1]
>>>for it in test:
>>>    it*=10
>>>    print( it, end = ' ')
40 30 20 10 

# for 语句可以修改列表元素吗?在上例中,每个元素都乘以10倍,那么列表元素本身是否修改了呢?
>>>test
[4, 3, 2, 1]    #元素本身并没有变化
# 要修改集合本身,需要使用索引
# 就列表而言,可以新建一个列表,之后逐个元素插入:
>>>res = []
>>>for it in test:
>>>    res.append( it * 10)

>>>res
[40, 30, 20, 10]

类似于其他语言中访问数组,Python中也提供了使用索引访问列表的操作。

>>>test = ['hello', 'world','I','like','Python']
>>>test[0]      #列表大小为5, 合法索引为0~4
'hello'
>>>print("The list's length is", len(test))     #函数len()可以获得列表的长度
The list's length is 5

# 不同于其他语言,Python 中负数也可以作为索引, -1代表列表的最后一个元素,以此推类:
>>>test[-1]
'Python'
>>>test[-5]
'hello'

Python还支持切片操作。切片类似于索引,也是访问列表元素的一种方式。
切片能够使得程序访问部分列表元素,并将给生成一个新列表:

索引语法 含义
[bgein: end] 包含 [begin, end) 的所有元素
[bgein: ] 包含 [begin, 列表末尾] 的所有元素
[: end] 包含 [列表开头, end) 的所有元素
[:] 包含列表所有元素。可用来复制/赋值
[begin?step] 参数 step指出了要包含指定区间内那些元素

例如:

>>>test[2:4]
['I', 'like']
>>>test[2:]
['I', 'like', 'Python']
>>>test[:4]
['hello', 'world', 'I', 'like']
>>>test[1:4:2]
['world', 'like']
>>>test[-3:]        #甚至可以使用负数
['I', 'like', 'Python']
>>>test[2:-1]       #甚至混合使用正负数
['I', 'like']
>>>last = 3         #甚至可以使用变量充当索引
>>>test[:last]
['hello', 'world', 'I']

注意,索引和切片虽然看起来相似,但存在重要的区别。
索引返回一个特定的元素。
切片返回一个列表。
由于切片返回一个列表,所以切片操作可以用来进行赋值/复制,且看:

# 假设存在以下列表
>>>x = [1,2,3,4,5]
#我们复制其中部分元素,例如:
>>>y = x[1:4]
>>>y
[2, 3, 4]
#由于python中列表是可变的,我们也可以对列表本身操作:
>>>x[-1] = "test"
>>>x
[1, 2, 3, 4, 'test']
>>>x[-1] = [6]      #注意二者区别
>>>x
[1, 2, 3, 4, [6]]
>>>x[-1:] = [6]
>>>x
[1, 2, 3, 4, 6]

在python中,在循环中可以使用索引的方式来修改列表元素,例如:

>>>test = [1,2,3,4]
>>>for i in [0,1,2,3]:  #需要生成一个长度为length的列表
       test[i] = test[i] * 10
    
>>>test
[10, 20, 30, 40]

# 如何简化整数列表?Python中提供内置函数 range来自动生成这组数字, 例如:
>>>for i in range( len( test)):
       test[i] *= 10
    
>>>test
[100, 200, 300, 400]

类似于切片,range也有三种使用方式:

语法
range(end)
range(start, end)
range(start, end, step)

例如:

>>>list( range(3))
[0, 1, 2]
>>>list( range(1,4))
[1, 2, 3]
>>>list( range(10, 16,2))
[10, 12, 14]

# range用例:
# 求阶乘:
def factorial(n):
    res = 1
    for i in range( 1, n + 1):
        res *= i
    return res

>>>print( factorial(20))
2432902008176640000

# 晒法求素数
def prime(n):   #求n以内的所有素数
    bool_list = [True] * (n + 1);
    res = [];
    for i in range( 2, n):
        if bool_list[i]:
            res += [i]
            for j in range( i * 2, n + 1, i):
                bool_list[j] = False
    return res

>>>print( prime(50))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

Python 提供了很多欢愉列表的强大操作,这里给出几个最重要的函数:

语法 含义
len 返回集合的长度
min 返回最小值
max 返回最大值

3.2 列表推导和枚举

Python 编程风格是提倡对可迭代对象(例如列表)进行迭代。从技术上来说,任何可以不断返回数据的东西都是可迭代对象。最常见的可迭代对象是集合。

# 迭代集合时,可以以来索引迭代:
>>>test = [1,2,3,4]
>>>for i in range( len( test)):
    print( test[i], end = ' ')
    
1 2 3 4 

# 但标准的python编程风格是:尽量不使用函数range
>>> for i in test:          # 更简洁且不用担心索引溢出
    print( i, end = ' ')
    
1 2 3 4 

从 python 2.0 起,引入了更好的解决方案——函数enumerate, 它可以直接迭代数据,同时获取索引号。
enumerate 是一个内置函数,生成一列的 index-item 对元组,其中 index 为移动计数器,item 为当前元素。
该函数基本语法如下:enumerate(iterable, start = 0), 其中iterable是可以不断从中获取下一个元素的数据集,例如列表、元素、字符串第二个参数是索引号的起始值,可选,默认为0.
例如:

>>>test = ["hello", "world","I","like","Python"]
>>>for i, str in enumerate( test):
    print(i,'.', str, sep = '')
    
0.hello
1.world
2.I
3.like
4.Python

formate 函数用来设置输出字符串中的一个或多个输出字段的格式。

>>>s = '{} results in {}'
>>>print( s.format(1, 2))
1 results in 2

花括号{}定义了与参数对应的打印字段,而其他文本为样板,将原样打印。
接下来进一步定制输出格式。例如左对齐、右对齐等。

语法 含义
{:>n} 将字符宽度设置为n个字符,并让内容右对齐。对于数字字段,等价于{:n}
{: 将字符宽度设置为n个字符,并让内容左对齐。对于非数字字段,等价于{:n}
{:^n} 将字符宽度设置为n个字符,并让内容居中

例如:

>>>test = [1,2,3,4,5,13,21,34,55,89,144]
>>>format_str = '{:>2}. {:>4}'  #创建两个打印字段,一个宽度为2且内容右对齐;一个宽度为4且内容右对齐。
>>>for i, item in enumerate(test):
      print( format_str.format(i, item))
    
 0.    1
 1.    2
 2.    3
 3.    4
 4.    5
 5.   13
 6.   21
 7.   34
 8.   55
 9.   89
10.  144

>>>format_str = '{:<2}. {:<4}'
>>>for i, item in enumerate(test):
      print( format_str.format(i, item))
    
0 . 1   
1 . 2   
2 . 3   
3 . 4   
4 . 5   
5 . 13  
6 . 21  
7 . 34  
8 . 55  
9 . 89  
10. 144 

简单的列表推导:

>>>test = []
# 使用for语句在列表末尾添加元素
>>>for i in range(1, 11):
       test.append( i * 2)
    
>>>test
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# 使用列表推导创建与test相同的列表:
>>>test2 = [i * 2 for i in range(1, 11)]
>>>test2
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# 使用列表推导来复制test:
>>>test3 = [ i for i in test]   
>>>test3
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
# 等价于    test3 = test[:]
# 也等价于  test3 = list( test)

二维列表:

# 假设要生成以下二维列表,我们有以下方法:
# 1 2 3
# 2 4 6
# 3 6 9

# 方法一:直接输入一维列表
>>>test = [1,2,3,2,4,6,3,6,9]
>>>test
[1, 2, 3, 2, 4, 6, 3, 6, 9]

# 方法二:利用循环生成一维列表
>>>test2 = []
>>>for i in range( 1,4):
       for j in range( 1,4):
           test2.append( i * j)
        
>>>test2
[1, 2, 3, 2, 4, 6, 3, 6, 9]

# 方法三:列表生成一维列表
>>>test3 = [ i * j for i in range(1,4) for j in range( 1,4)]
>>>test3
[1, 2, 3, 2, 4, 6, 3, 6, 9]

# 打印:
>>>for i, item in enumerate( test, 1):
       print(item, end = ' ')
       if i % 3 == 0:
           print()
          
1 2 3 
2 4 6 
3 6 9 

扩展:如果想要打印 m*n 的表格,例如:

1
2 4
3 6 9

该如何处理?
其实很简单:需要添加一些条件:

>>>test = []
>>>for i in range( 1,4):
       for j in range( 1, 4):
           if i >= j:
               test.append( i * j)
            
>>>test
[1, 2, 4, 3, 6, 9]

# 列表生成法:
>>>test2 = [ i * j for i in range(1, 4) for j in range( 1,4) if i >= j]
>>>test2
[1, 2, 4, 3, 6, 9]

# 打印:
>>>i = 0
>>>j = 1
>>>for t in test:
       print( t, end = ' ')
       i += 1
       if i == j:
           print()
           j += 1
           i = 0
        
1 
2 4 
3 6 9 

# 通过列表生成,我们来看一个简化版(代码上)的筛法求素数:
# 假设求 1 ~ 20 以内的所有素数:

# 首先用record记录1~20内所有合数
>>>record = { j for i in range( 2,20) for j in range( i * i, 20, i)}     # {}代表record是一个集合,集合可以减少
#枚举不在record中的元素,则得到相应区间的素数
>>>res = [ i for i in range( 2, 20) if i not in record]
>>>res
[2, 3, 5, 7, 11, 13, 17, 19]

集合(set): 在Python中,集合用{}表示,除此之外创建方式与列表几乎相同。
主要来看集合与列表的差别:1. 集合中元素是独一无二的,列表中元素可以存在重复;2. 集合中,只关注元素的个数/内容,而不关注元素的排列顺序,而列表中元素顺序也需要考虑;3. 集合是默认有序,即输出集合时会按照某种顺序排列,而列表不会。

>>>test1 = {1,2,3}      #集合
>>>test2 = [1,2,3]      #列表

# 集合元素独一无二
>>>test3 = {1,1,2,3}
>>>test3
{1, 2, 3}

# 集合不关注元素顺序
>>>test1 == {2,1,3}
True

# 集合会按照某顺序输出
>>>{3,2,4,1}
{1, 2, 3, 4}

4.1 字符串

文本字符串由可打印的字符组成。


如何创建字符串?Python中可以使用引号来创建:

>>>a = 'hello world'        #可以使用单引号
>>>b = "hello world"        #也可以使用双引号
# 以上二者没有差别,作用相同。
# 但不能混合使用单引号与双引号:
# c = "test'        # ERROR

# 单引号的缺点:
>>> c = 'I\'m a boy'    #此时,可以使用转义字符,也可以使用双引号
# 双引号的缺点:
>>>c = "he said \"good\" "  #此时,可使用转义字符,也可以使用单引号

# 如果在字符串中同时嵌入两种括号,该如何处理?
# 可以创建 字面引用,即按原样处理三引号内的所有字符:
>>>d =''' he said "It's odd" '''
# 三引号另外一个功能:可以原样输入所有字符,包括换行符:
e = "this is a
test string"
>>>print(e)
this is a
test string

注意,python无法改变字符串一部分,例如:

>>>test5 = 'hello'
>>>test5[0] = 'H'  # TypeError: 'str' object does not support item assignment
# 不能改变字符串内容,但可以将全新字符串赋值给变量:
>>>test5 = 'Hello'

字符串中的操作:索引与切片。
很多用于列表的索引和切片操作也可以用于字符串。在Python中,合法索引都为0 ~ N-1, 其中N是需要访问对象的长度。

语法 含义
[index] 获得指定索引出的字符
[begin:end]
[begin:]
[:end]
[:]
[begin?step]

例如:

>>>s = 'hello'
>>>s[0]
'h'
>>>s[1:3]
'el'
>>>s[-1]  #与列表一样,也可以使用负数,-1代表倒数第一个字符
'o'
>>>s[:-2] #从下标0开始,到倒数第二个字符(不包含)
'hel'

在Python中,支持字符串与数字之间的相互转换。
字符串转为数字,例如:

>>>test = int( "321")
>>>test
321
>>>test + 2
323

>>>test = float("123.321")
>>>test * 2
246.642

# 字符串与整数不能用于算数运算:
>>>3 + '3'  # TypeError: unsupported operand type(s) for +: 'int' and 'str'
# 但是字符串可以与整数val相乘,表示val个字符串相加:
>>>'2'*3
'222'

数字也可以转换为字符串:

>>>s = str( 123 + 456)
>>>s
'579'
>>>len(s)
3

Python中对于字符串的操作还有很多。
我们可以剔除多余的字符,

  1. strip可以用于字符串剔除开头/末尾的字符并返回得到的字符串;
  2. split可以让字符串返回一个列表,其中包括拆分得到的字符串;
  3. '+'可以拼接字符串。
  4. join可以用于分割字符串,将一个字符串列表作为参数,并返回合并得到的字符串。
  5. len可以返回字符串、列表及其他集合的长度。
# strip
>>>test = '0000123456789000'
>>>test.strip('0')#括号中是一个字符串,指定了要剔除哪些字符
'123456789'

# 注意,strip将在其他文本后删除括号内指定的每一个字符(不要求这些字符按顺序出现)
>>>test2 = '***+++Hello*+!World+++***!!!'
>>>test2.strip('*+!')
'Hello*+!World'
>>>test3 = 'a***+++Hello*+!World+++***!!!'
>>>test3.strip('*+!')
'a***+++Hello*+!World'


# split 
# split将会依据括号内参数(默认为空格)来拆分字符串为列表
>>>test4 = "There are three people: Tom Bob Joe"
>>>test4.split()
['There', 'are', 'three', 'people:', 'Tom', 'Bob', 'Joe']
>>>test4 = "There are three people: Tom,Bob,Joe"
>>>test4.split(',')
['There are three people: Tom', 'Bob', 'Joe']


# +
>>>test5 = 'I want to'
>>>test6 = 'coding'
>>>test5 + test6
'I want tocoding'       #注意,拼接时不会自动插入空格
>>>test5 + ' ' + test6  #需要手动添加空格
'I want to coding'
# 支持赋值和拼接组合在一起的运算符 +=
>>>test5 += ' ' + test6
>>>test5
'I want to coding'


# join
# '+'虽然也能合并多个字符串,但python中join的效率要高很多
>>>test7 = ['I', 'like','coding']
>>>' '.join( test7)
'I like coding'
>>>'---'.join(test7)
'I---like---coding'
# 注意,join虽然可以连接列表,但它是一个字符串方法。

4.2 操作字符

本节给出了一些字符串的操作。


Python中变量名的限制很少,但是为了提高代码的可读性,我们应当遵守一些命名规定:

模式 数据类型
xxx_str, s, s2 Python字符串
xxx_ch 只包含一个字符的字符串
xxx_list Python列表

在Python中,用于字符串的函数和方法很多,因此想要全部记住很困难。
在交互式系统下,可以使用help(str)来输出语法摘要以便寻求帮助。


对于字符,最有用的操作之一是大小写转换/检查等操作。
对于字符串str,我们有以下操作:

方法 行为
str.isupper() 若字符串至少含有一个字符且都为大写,返回True; 否则返回False
str.islower() 若字符串至少含有一个字符且都为小写,返回True; 否则返回False
str.istitle() 若字符串中每个单词至少含有一个字符且第一个字母为大写,返回True; 否则返回False
str.isalpha() 若字符串中每个字符都是字母,返回True; 否则返回False
str.upper() 将字符串中每个字符都转换为大写(非字母字符不变)
str.lower() 将字符串中每个字符都转换为小写(非字母字符不变)

例如:

>>>s = 'TheTestString'
>>>s.isupper()  #对整个字符串操作
False
>>>s[0].isupper()   #对单个字符操作
True
>>>s.islower()
False
>>>s.istitle()  #因为Test中T在The的后面,所以为False
False
>>>'The'.istitle()
True
>>>'The Test String'.istitle()
True
>>>s.isalpha()
True
>>>s.upper()
'THETESTSTRING'
>>>s.lower()
'theteststring'

在类C语言中,字符是由ASCII表示的。同样,Python中也可以进行ASCII与字符的转换:
函数chr()可以将ASCII编码转换为字符。
还书ird()可以将字符转换为ASCII编码。

>>>chr(65)
'A'
>>>ord('a')
97

5.1 高级函数技术

多个参数:所有的编程语言都支持传递多个参数。
函数可以接受任意数量的参数,唯一的要求是函数定义总的参数数量必须与调用函数时指定的参数数量相同。
当有多个参数时,用逗号分割它们。
例如:

def avg(a, b):
    return ( a + b)/2
    
>>>print(avg(1,4))
2.5

如何让函数永久地修改传递给它的变量的值?
例如,在C++中,可以通过引用方式传递。但 Python 中并没有这样的概念,例如:

def add(a):
    a += 1
    return a

>>>test = 1
>>>print(add(test))
2
>>>print(test)
1

# 为什么不会改变变量的值?
# 当test传递给函数时,函数获得一个指向1的引用。
# 但新的值赋值给test时,它与旧数据的关联断开

# 如何改变参数?考虑到python中列表可变,我们可以在列表上下文章;
def add(a):
    a[0] += 1
    a.append(3)
    a += [4]

>>>test = [1]
>>>add(test)
>>>print(test)
[2, 3, 4]
# 但是注意,以下情况行不通:
#这种语法直接给列表变量本身赋值,会导致创建一个新的列表
def add(a):
    a = a + [2]     


>>>test = [1]
>>>add(test)
>>>print(test)
[1]

另外注意,在定义/调用函数时,Python不会检查参数类型,但是一旦类型不兼容,Python会在执行期间返回错误。


如何让函数返回多个值?
在 Python 中,只需要在 return 语句中包含一系列用逗号分割的值即可,例如:

def add(a):
    return a, a + 1, a + 2  #任意数量均可

>>>test = 1
>>>print(add(test))
(1, 2, 3)

Python支持两种参数传递方式。
第一种是具名参数,即通过名称指定参数,例如:print(1,2,3, sep = '-', end = '!\n') #输出1-2-3!, 参数 sep 和 end 比较特殊,因为在函数调用中指定了它们的名称。具名参数必须在参数列表末尾指定。
第二种是位置参数,即不指定参数名称。例如:

def add(a, b):
    return a + b
# 函数调用:
>>>add( 1,2)

# 如果愿意,可以给每个参数值指定名称
def sub( a = 1, b = 2)
    return a - b
>>>sub()    #默认为 sub(1,2)
# 以上方式又称为默认参数,如果不指定sub括号里参数的值,则默认取指定的值。
# 同时,也可以指定其中某个/某些参数的值,例如:
>>>print(sub(b = 3))
-2

虽然Python提供了很多功能强大的内置函数,但是还是有一些函数是由Python模块提供的。
Python模块提供了额外的功能扩展,且自带了一些模块,例如math和random.

# 从模块中导入函数的方式:
# 方式1:
import module_name  
# module_name 是模块名,该方式可以使用模块中任意函数,但必须使用模块名进行限定,例如:
>>>import random
>>>random.randint(0,10)
7

# 方式2:
form module_name import function_name
# 从模块中导入特定的函数,此时引用函数无需使用模块名
>>>from math import sqrt
>>>sqrt(7)
2.6457513110645907

# 方式3:
from random import *
# 导入模块中的所有函数,这样引用函数不用进行限定,但是可能会发生冲突

6.1 局部变量和全局变量

编程中一个主要问题是通信,即如何在函数之间共享信息。
最直接的方式是使用全局变量。


在全局变量和局部变量方面,Python有其独特的规则。
首先看局部变量:局部变量只对一个函数可见,即若存在同名的全部变量和局部变量,则函数只能看到这个变量的局部版,无法使用它来影响外部的语句。例如:

def fact( n):
    prod = 1
    #下处 i 为局部变量,fact只能看到该i的值
    for i in range( 1, n+1):    
        prod *= i
    return prod

>>>i = -1
>>>n = fact(5)
>>>print("i =",i, "n =",n)
i = -1 n = 120

规则:局部变量优先于全局变量。这种特性使得创建和维护程序更容易。


再看全局变量:全局变量在全局作用域中赋值,也可以在函数中改变值。
当函数需要共享信息时需要全局变量。全部变量的生命周期与程序一样长,在多个函数中都能看到。
是什么决定了变量是全局变量还是局部变量?规则如下:1. 变量名出现在函数定义中,即在函数中通过赋值创建了它,则该变量为局部变量;2. 若变量var的局部版本不存在,就使用全部变量var(如果全局变量存在的话);3. 若在函数中使用global var 语句,python会寻找var的全局版本。


关键字global的作用是让函数无法识别指定变量的局部版本,而寻找全局版本。
例如:

def func( n):
    global my_thing
    my_thing = 0    # 创建全局变量my_thing
    locvar = 0      # 局部变量

your_thing = 0      # 创建全局变量your_thing
# 上例创建了两个全局变量 my_thing 和 your_thing. 
# my_thing 是在函数内部创建的,但由于关键字global, 是其作用域为全局。
# your_thing 在模块级函数外面创建,因此也是全局变量

7.1 文件操作

磁盘文件可以分为两大类:文本文件和二进制文件。
文本文件中的所有数据都是有可打印的字符组成,读写操作与读写控制台类似;
二进制文件包含原始数据,通常以人们看不懂的格式存储数据。Excel能正确读入Excel创建的二进制文件,Outlook能读懂Outlook创建的二进制文件。
python 提供的文件读写工具十分简单,其中某些工具利用了可迭代的概念。


在读写文件之前,首先要确定磁盘中存在那些文件。
python中提供了一份功能简答的模块——os模块(operating system 的首字母缩写),它可以与文件/目录交互。
下面给出一些操作:

方法 行为
import os 导入模块os
os.listdir 列出目录内容。返回一个列表
os.chdir(string) 切换到string目录
os.getcwd() 获取当前目录。返回一个字符串

例子:

# 导入模块
>>>import os
# 查看帮助
>>>help( os)
# 列出当前目录的内容:
>>>file_list = os.listdir()
>>>for item in file_list:
       print(item)
# 以下三行是输出的呢绒
learned.py
venv
.idea    
# 使用len函数来确定当前目录下有多少文件:
>>>len( file_list)
3
# 使用函数chdir可以切换目录,该函数接受一个字符串参数
>>>os.chdir('venv')
# 使用函数getcwd查看当前目录的完整路径
# getcwd 是 GET Current Working Directory 的首字母缩写
>>>os.getcwd()
'/Users/vsym/PycharmProjects/test/venv'
# getcwd函数返回的是一个字符串

了解基本操作之后,现在来看文件的输入/输出操作过程:1. 打开文件;2. 读取/写入信息;3. 关闭文件。
首先看打开文件,当程序员让Python将信息写入/读取某个文件时,必须将其打开。
再看读写文件,读写操作是文件操作的关键,用下例说明。
最后看文件关闭,当写入操作完成时,可能只有一部分写入了磁盘文件,关闭文件将最后的写入操作指定的数据真正存储到文件中。

# 打开文件:假设test.txt为空白文件
f = open('test.txt', 'w')
# 打开test.txt文件,'w'位置处为打开方式,'w'指可以写入

# 写入文件:
f.write('Hello World')
# 此时打开test.txt, 有:
Hello World
# 写入第二条语句
f.write('I like Python')
# 此时打开test.txt, 有:
Hello WorldI like Python
# write并不会自动添加换行符, 若想换行则应该使用\n
f.write('\nThis is a test line')
# 此时打开test.txt, 有:
Hello WorldI like Python
This is a test line

# 关闭文件
f.close()

#------------------------------#

# 打开文件
f = open('test.txt', 'r')   #'r'代表读取
# 以上等价于 f = open('test.txt'), 默认就是读取文本
# 注意,读入文件必须保证文件存在

# 读取文件
for i in f:
    print(i, end = '')
# 读取完文件后,文件指针位于文件末尾
# 因此要想多次读取同一个文件,需要以下操作:
f.seek()    #将文件指针重置到文件开头

# 关闭文件
f.close()

在某些编程语言中,若打开文件失败时会返回一个null指针。同样,在python中,打开文件失败也会引发异常,具体来说类型为 FindNotFoundError 的运行阶段错误。
要处理异常,需要编写异常处理控制结构,其格式为:

try:
    try_statements
except error_tyoe:
    except_statements
# 在某些情况下,可能需要多个except,用来捕获不同类型的异常
# try_statements 在任何情况下都会执行
# 注意,try_statements 在某些情况下也可能因异常而中断
# 当且仅但 try_statements 执行期间发生异常,才会执行 except_statements.
# 无论是否异常,都会继续执行 try/except 后的语句

例如:

try:
    f = open( 'test.txt')
    print( f.read())
    f.close()
except FileNotFoundError:
    print('This is not exist')
    
# 当 test.txt 存在时,会输出其内容
# 当 test.txt 不存在时,会输出This is not exist.

现在考虑以下两条语句 open('test.txt', 'w'), open('test.txt', 'r'), 在被打开文件的后面,都跟随着一个字符,用来表述不同的打开模式, 下面来看不同字符对应的模式:

模式 描述
r 以读取方式打开文本文件,默认模式
w 以写入方式打开文本文件
w+ 以写入方式打开文本文件,该模式不会破坏既有内容
rb 以读取方式打开二进制文件
wb 以写入方式打开二进制文件
w+b 以写入方式打开二进制文件,该模式不会破坏既有内容

8.1 字典和集合

字典是一种映射,例如学号对应的学生、首都对应的国家等。Python使用dict来表示字典类型,其格式形如:

dictionary[key] = value

其中key被称为键,value被称为值。
字典元素通过key访问,而不是通过位置访问。因此,字典中的每个键都是独一无二的,且给键选择数据类型之后,其余的所有键的类型也必须与之相同。另外,键一旦确定后,不可改变,因此整数、浮点数、字符串类型等可以用作键,但列表不能。
下面是关于字典的例子:

# 建立一个字典用来记录学号与学生姓名对应关系

# 创建字典, 这里学号与姓名均为字符串格式
>>>dic = {'001' : "Alis", '002' : "Bob", '003' : 'Cook'}
# 查看当前字典
>>>dic
{'001': 'Alis', '002': 'Bob', '003': 'Cook'}

# 如何添加一个键-值?
# 假设有位新同学 Dim.
>>>dic['004'] = 'Dim'  
>>>dic
{'001': 'Alis', '002': 'Bob', '003': 'Cook', '004': 'Dim'}

# 如何改变已经存在的键的值?
# 假设Alis同学退学,新来一名Ellen同学
>>>dic[ '001'] = 'Ellen'
>>>dic
{'001': 'Ellen', '002': 'Bob', '003': 'Cook', '004': 'Dim'}

# 如何访问键所对应的值?
# 假设查询'003'号同学的姓名
>>>dic['003']
'Cook'

# 注意,如果访问了不存在的键,则会发生错误
>>>dic['005']    #KeyError, 不存在该键
# 如果使用不同类型的键访问,也会发生错误
>>>dic[001]      #SyntaxError, 语法错误

# 如何遍历字典?可以使用循环
>>>for key in dic:
       print(key, '\t', dic[key])   #'\t'用来对齐
    
001   Ellen
002   Bob
003   Cook
004   Dim

Python 字典支持大量的字典操作,这些操作与列表支持的操作不尽相同,但也有一些共同点,例如支持len函数。
在交互式环境中,可以使用help(dict)来获悉字典功能。
一个十分有用的字典操作是items, 它以列表的方式返回字典的全部内容。例如:

# 定义字典
>>>dic = {'001' : "Alis", '002' : "Bob", '003' : 'Cook'}
>>>dic.items()
dict_items([('001', 'Alis'), ('002', 'Bob'), ('003', 'Cook')])
# dict_items 不是列表,是一个可迭代对象

Python中与字典紧密相关的数据结构是集合(set), 对于集合,有几点说明:1. 集合包含的不是键,而是元素;2. 集合的元素是独一无二的;3. 集合无视顺序。
集合使用大括号 “{}” 表示(大括号有时被成为“集合括号”)

>>>nums_set = {3,4,5,1,2,6,7,1}
>>>nums_set
{1, 2, 3, 4, 5, 6, 7}

# 特别注意,一旦使用一对空的大括号
>>>test = {}
# 此时Python会认为以上是一个空字典

# 验证
>>>type( test)
<class 'dict'>
>>>type( nums_set)
<class 'set'>

# 创建空集合的方式
>>>test2 = set()

集合与列表有两点不同:

  1. 集合中元素是独一无二的,正如上述代码nums_set = {3,4,5,1,2,6,7,1}, 当输出nums_set 其中的两个1会被忽略成一个。
  2. 集合中元素的排列顺序无关紧要,nums_set 输出为1,2,3,4,5,6,7.

集合支持大量独特的操作:

>>>nums_set = {3,4,5,1,2,6,7,1}
>>>nums_set.remove(1)   # 删除元素1
>>>nums_set.add(8)      # 添加元素8
>>>nums_set
{2, 3, 4, 5, 6, 7, 8}

# 两个集合相互操作
>>>nums2 = {1,3,5,7,9}
>>>nums_set - nums2     # 差集
{8, 2, 4, 6}
>>>nums2 - nums_set     # 差集
{1, 9}
>>>nums2 | nums_set     # 并集
{1, 2, 3, 4, 5, 6, 7, 8, 9}
>>>nums2 ^ nums_set     # 
{1, 2, 4, 6, 8, 9}
>>>nums2 & nums_set     # 交集
{3, 5, 7}

9.1 矩阵:二维列表

在Python中,可以通过初始化来轻松地创建一个小型矩阵,并对其进行一系列操作,例如:

# 创建矩阵, 为通俗化,这里创建一个不规则矩阵
>>>list2D = [[-3,-2,-1],[0,1],[2,3,4,5,6]]
>>>list2D
[[-3, -2, -1], [0, 1], [2, 3, 4, 5, 6]]

# 访问矩阵内元素
>>>list2D[0][2]
-1
>>>list2D[0][33]    # IndexError, 越界

# 获得矩阵长度
>>>len( list2D)     # 矩阵的行数
3
>>>len( list2D[2])  # 第三行元素个数
5

# 乘法
# 首先看列表
>>>test_list = [1]
>>>test_list * 9        # 相当于 9 个 test_list 相加
[1, 1, 1, 1, 1, 1, 1, 1, 1]
# 扩展到矩阵
>>>test_list2D = [[0] * 3] * 2
>>>test_list2D
[[0, 0, 0], [0, 0, 0]]

# 但是!以上方法存在一个重大问题!
# 以上方法在Python中只会进行创建引用,例如:
>>>test_list2D[0][1] = 2
>>>test_list2D  # 会造成整体影响
[[0, 2, 0], [0, 2, 0]]

如何创建 N * M 矩阵?
不同于其他语言,Python不存在数据声明的概念,这也会导致Python在创建矩阵时候存在一些麻烦。
Python中,创建多维矩阵必须从空白开始创建,其语法格式如下:

# 格式:name[ [val] * cols for i in range(rows)]
# 例如:创建3行3列的矩阵
>>>matrix = [ [0] * 3 for i in range(3)]
>>>matrix
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]

# 例,现在打印一个乘法表
def mult_tab():
    rows = cols = 8
    matrix = [ [ (i + 1) * ( j + 1) for j in range( cols)
                                    if j <= i]
                                    for i in range( rows)]
    for i in range( rows):
        for j in range( cols):
            if i >= j:
                print('{:<3}'.format(matrix[i][j]), end = '')
        print() #换行符

>>>mult_tab()
1  
2  4  
3  6  9  
4  8  12 16 
5  10 15 20 25 
6  12 18 24 30 36 
7  14 21 28 35 42 49 
8  16 24 32 40 48 56 64 

对于 N*M 矩阵,如何进行旋转?方法有很多:性能比较好的有原地旋转;也可以图方便另外开辟一个新矩阵,之后进行替换处理。
由于O(NM)空间的解决方法过于简单,这里不再赘述;这里给出一个方阵的原地旋转的例子:

# 首先创建一个方阵:
def rotate():
    size = 3
    matrix = [ [ i - j for j in range(size)]
                       for i in range(size)]

    # 打印原本的矩阵
    for i in range( size):
        for j in range(size):
            print('{:>3}'.format( matrix[i][j]), end = '')
        print()

    # 原地旋转有两个步骤:上下反转、左右反转
    # 上下
    for i in range(size//2):
        for j in range( size):
            temp = matrix[i][j]
            matrix[i][j] = matrix[ size - i - 1][j]
            matrix[size - i - 1][j] = temp
    # 左右
    for i in range(size):
        for j in range( size//2):
            temp = matrix[i][j]
            matrix[i][j] = matrix[ i][ size - j - 1]
            matrix[ i][ size - j - 1] = temp

    print()
    # 打印旋转后的矩阵
    for i in range( size):
        for j in range(size):
            print('{:>3}'.format( matrix[i][j]), end = '')
        print()
        
>>>rotate()
  0 -1 -2
  1  0 -1
  2  1  0

  0  1  2
 -1  0  1
 -2 -1  0

10.1 类和对象

在Python中,类、对象和面向对象范式十分重要,它们是Python的基石。


首先考虑,对象是什么?其实,从本文一开始,就一直在使用Python对象。例如,字符串就是对象,它们支持方法。
方法类似于函数,通常用’.'应用于特定的对象。实际上,方法所做的就是向对象阿发送消息,而对象据此作出相应。例如:

>>>test = " 012345   "
>>>test.strip()
'012345'
# 上述语句中对一个字符串对象调用方法strip, 对象将开头和末尾的空格删除,并返回得到的字符串。

在Python中,所有的数据项都是对象,这包括 int, float, 字符串等内置数据类型,也包括自定义的数据类型。它们都支持方法,例如:

>>>(22).bit_length()    # 指出int类型对应的二进制的位数
5

在Python中,所有的数据项都是对象,而每个对象都是某个类的实例。通过创建自定义类,可以定义全新的数据类型。
我们分为以下几个问题来研究:1. 如何定义简单的类?2. 如何使用类来创建对象?3. 如何给对象添加数据?4. 如何编写方法?


与C++类似,Pyhton中也存在class关键字,其语法也类似:

# 定义简单的类
class class_name:
    method_definitions
    
# 使用类来创建对象
obj_name  class_name( args)

一般而言,类包含领个或者多个方法的定义。我们通过例子说明:

# 定义一个不包含任何定义的类:
class Dog:
    pass    # pass表示空操作
# pass类似于占位符,指暂时不需要,等需要时补充

# 使用类来创建一个对象,例如:
my_dog = Dog()  # 即使没有参数,也应该添加括号

# 给对象添加数据:任何对象都可以在运行阶段直接添加数据
my_dog.name = "Skyler"
my_dog.age = 7

print( "My dog's name :", my_dog.name)

# 以上添加数据方法存在局限性:
# 只能给特定对象添加数据而无法让它的class的所有对象都包含这些实例变量

# 编写方法
# 假设重新定义 Dog 类:
class Dog:
    def speak(self):
        print('ruff, ruff!')

my_dog = Dog()
my_dog.speak()
# 输出“ruff, ruff!”

Python中有很多特殊的命名方法,这些名称实际上是保留字,它们的开头和末尾都是两个下划线(__).
按照命名规则,开头两个下划线表示成员是私有的(虽然Python并没有私有访问权限)。
方法 _init_ 是最重要的方法之一,它是一个初始化方法,在创立对象之后被立即调用。这是创建实例变量的理想之地,它确保同一类的所有对象都支持同一组变量。其通用语法如下:

class class_name:
    def __init__(self, other_args):
        self.var_name = arg
        self.var_name = arg
        #...

例如,还是针对 Dog 类别:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 有了以上定义,我们可以以下列形式创建Dog对象
my_dog = Dog('Steve',5) # 位置参数
your_dog = Dog( age= 2, name = 'Sunday') # 关键字参数

当然,除了 int/ 之外,我们还可以编写其他方法。
方法能够让对象向用户提供服务。对于普通函数,需要指定通过参数传递给函数的所有信息,这样可能会导致参数列表很长;但调用方法时,假定对象包含所需要的大部分信息,方法也可以包含参数,但通常参数列表比较短。

另外,另一特特殊方法是 __str__, 它通过返回一个字符串来实现,决定了如何将对象转化为字符串格式。

还有其余的包括 __add_, __eq_, _sub_ 等特殊方法。


对于我们定义的类,我们也可以使用 help() 函数,例如对于 Dog 类:

help( Dog)

'''以下为注释输出
Help on class Dog in module __main__:

class Dog(builtins.object)
 |  Dog(name, age)
 |  
 |  Methods defined here:
 |  
 |  __init__(self, name, age)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
'''

# 上述帮助信息内容不多,但提供了以下信息:
# Dog 被视为一个类
# 在Python 3.0 中,类隐式地继承根类 object
# Python 为每个类存储了一个数据字典
# 我们可以在类中使用 ''' ''' 插入注释来添加help内容

C++语言中,提供最方便的功能之一是重载:可以编写多个用于初始化创建对象的函数,这些函数以不同的方式初始化对象,而具体调用哪个函数取决于用来初始化的数据。
在 Python 中也有相同的功能,只需要:1. 检查传递给函数的参数类型;2. 根据参数的类型采取不同的措施。

要想检查参数类型,这里推荐使用 isinstance 函数,其格式如下:

isinstance( object, type_name)
# object 为被检测对象
# type_name 为待检测类型

# 例:
>>>isinstance( s, str)
True
>>>isinstance('qwe', float)
False
>>>isinstance('qwe', str)
True
>>>isinstance(1.2, int)
False
>>>isinstance(2, int)
True

接下来看变长参数列表,这是重载必不可少的一部分,其语法格式如下:

def function_name( *args):
    statements
# 参数名可以是任何合法名称(这种情况下写为args)
# '*' 不同于 C/C++ 中的含义。
# *args 指传输的所有参数都将塞进一个列表中。


# 例如:
def print_all( *args):
    for thing in args:
        print(thing);

>>>print_all(1,2.3,'4', False)
1
2.3
4
False

继承:当一个类继承另外一个类时,它将自动获得后者的方法和属性,对于这些方法和属性可以保持不变,也可以重写。
一个类继承另一个类,前者成为子类,后者称为父类。
继承的基本语法如下:

class class_name( base_class):
    class_definitions

继承是继承的那些东西?

  1. 在Python中,将继承所有的方法,包括诸如 _int_ 等特殊方法(注意,这不同于C++, 因为C++中不能继承构造函数)。
  2. 继承所有类变量。
  3. 实例变量关联的是实例,而不是类,不一定会被继承。但是若这些变量是使用 _int_ 或者其他方法创建的,则会被继承。

例如:

# 首先创建一个Pet类
class Pet:
    def __int__(self, name, age):
        self.name = name
        self.age = age
    def introduce(self):
        print("name :",self.name, "age :", self.age)

# 创建Pet类的一个子类:Dog类
class Dog( Pet): # 继承Pet类
    # 添加方法bark
    def bark(self):
        print(self.name, ":Ruff, ruff!")

>>>my_Dog = Dog()
>>>my_Dog.name = 'Ruby'
>>>my_Dog.age = 3
>>>my_Dog.introduce()
name : Ruby age : 3
>>>my_Dog.bark()
Ruby :Ruff, ruff!

实例变量是与对象关联的变量,虽然所有的实例都可以有相同的蓝图,但是每个都将维护自己的值。因此实例变量不是共享的。
这里介绍类变量。
类变量是由同一个类的所有成员共享的变量。这是一种高级技术。
例如:

class Dog:
    num_dogs = 0    # 创建类变量
    def __init__(self, name):
    self.name = name
    Dog.num_dogs += 1   # 方法访问类变量时,不用self.var
                        # 而使用语法 class.var
    
# 类名句点表示法访问该变量
>>>print( Dog.num_dogs)
0
# 使用类的对象以类似的方法表示
>>>d = Dog('Alisa')
>>>print( d.num_dogs)
1

类还可以包含类方法和静态方法。这两种方法很想,都是由类的所有实例共享,且不能通过实参调用。差别在于静态方法不接受额外的参数,而类方法接受一个指向本身的额外参数(按约定为cls).
二者的语法如下:

# 静态方法的语法:
@staticmethod
def method_name( args):
    statements
    
# 类方法
@classmethod
def method_name(cls, args):
    statements

类变量还有其他用途,包括:1. 定义对类来说很有用的变量,例如PI; 2. 定义实例变量的默认值。
例如:

# 定一个Circle类,和一个包含计算圆面积的函数
class Circle:
    PI = 3.1415926

    def __init__(self, r):
        self.r = r

    def get_area(self):
        return Circle.PI * self.r * self.r

>>>print( Circle(3).get_area())
28.274333400000003

11.1 Python 高级技术

Python是一个很好入门的语言,但深入理解却有些困难,这里介绍一些Python的高级功能。
所谓高级功能,就是无需学习它们来高效地白菜昵称,但是可以使用它们做一些令人惊讶的事情。


我们已经对此提及:Python 是基于可迭代对象的概念。在 Python 中,可以创建一个自定义的可迭代对象,将其用于for循环或其他地方。
要创建自定义的可迭代对象,只需要将 return语句替换为yield value.
例如:

# 打印斐波那契数列
def print_fibos(n):
    a = 1
    b = 0
    count = 1
    while count <= n:
        a, b = a + b, a
        count += 1
        print(a, end = ' ')
        
# 打印到第10个
>>>print_fibos(10)
1 2 3 5 8 13 21 34 55 89 

# 将函数变为生成器(进而成为可迭代对象)
# 只需要将print替换为 yeild语句
def print_fibos(n):
    a = 1
    b = 0
    count = 1
    while count <= n:
        a, b = a + b, a
        count += 1
        yield a

>>>for i in print_fibos(10):
       print(i, end = ' ')
       

在任何需要可迭代对象的地方,都可以使用迭代器,包括for循环。
例如在上述代码中的斐波那契数列,我们可以使用 next 函数,它被用来从可迭代对象中获得“下一个”元素,例如:

def print_fibos(n):
    a = 1
    b = 0
    count = 1
    while count <= n:
        a, b = a + b, a
        count += 1
        yield a

>>>test = print_fibos(10)
>>>print( next( test))
1
>>>print( next( test))
2
>>>print( next( test))
3
>>>print( next( test))
5

特性property:
要给特性编写获取方法,可在类中添加如下定义:

@property
def property_name( self):
    statement
    return value

每当用户试图获取具体特性的值时,都将自动调用其获取方法。
例如:

# 下面的类包含特性a, b, c:
class MyClass:
    def __init__(self):
        self._a = self._b = self._c = -1
    #'_'指出应该将它们视为私有的
    
    @property
    def a(self):
        return self._a  # 返回_a的值

    @property
    def b(self):
        return abs(self._b) # 返回_b的绝对值

    @property
    def c(self):
        return self._c % 2 == 0 # 返回_c是否为偶数

>>>my_obj = MyClass()

# 当遇到以下语句时,将调用相应的获取方法
>>>print(my_obj.a)
-1
>>>print(my_obj.b)
1
>>>print(my_obj.c)
False

设置方法是获取方法的反面。
要为特性编写设置方法,可以在类中添加如下定义:

@property_name.setter   # 设置方法
def property_name(self, new_value):
    statements

下面演示如何为特性a, b, c编写设置方法:

@a.setter
    def a(self, new_a):
        self._a = new_a

    @b.setter
    def b(self, new_b):
        self._b = abs( new_b)

    @c.setter
    def c(self, new_c):
        self._c = new_c % 2 == 0

当然,通常同时给特性定义获得方法和设置方法。但也存在一些特殊情况,例如让特性只读时,只可以编写获取方法,而不编写设置方法。
示例:

class MyClass:
    def __init__(self, new_b):
        self.b = new_b  # 调用设置方法

    @property       # 特性b的获取方法
    def b(self):
        return self.__b

    @b.setter       # 特性b的设置方法
    def b(self, new_b):
        self.__b = abs( new_b)  #将参数的绝对值赋个私有变量__b

>>>my_obj = MyClass( -5)
>>>print( my_obj.b)
5

装饰器是包装其他函数的函数。
要理解装饰器,首先需要看看Python中如何定义嵌套函数,例如:

def outer(n):
    print( "I am a outer")
    def inner():
        print("I inherited", n)
    inner()

>>>outer(3)
I am a outer
I inherited 3

# 现在改变以下outer的定义:
def outer(n):
    print( "I am a outer")
    def inner():
        print("I inherited", n)
    f = inner  # 现在f是inner的别名
    f()

>>>outer(3)
I am a outer
I inherited 3

# 现在再做一些改变
def outer(f):   # outer接受一个类型为函数的参数
    def inner():
        print('I am doing extra stuff')
        f()
        print('Doing more extra sturff')
    return inner
# outer 就是包装函数
def f1():   
    print('Hi')

>>>g = outer(f1)   #现在g是新函数的别名, 即包装函数的别名
>>>g()
I am doing extra stuff
Hi
Doing more extra sturff

Python 中装饰就是让一个函数名指向其包装版。
例如:

def my_decorator(f):
    # 讲一个函数作为参数,创建并返回名为wrapper的新版本:
    def wrapper():
        print("I am doing extra stuff")
        f()
        print("Doing more extra sturff")
    return wrapper

# 这个装饰器创建一个新函数并返回它
# 我们可以将它赋给原始函数的名称

# 应用装饰语法
@my_decorator   #新语法
def hello():
    print("Hi")
# 运行后,Python会将这些代码转换成以下:
'''
def hello():
    print("Hi")
hello = my_decorator(hello)
'''

以下语法总结了装饰器的工作原理:

@decorator_name
def func( args):
    statements
    
# 这些代码行将被转化为以下:
def func( args):
    statements
func = decorator_name( func)

# 若被装饰的函数有参数/若返回值时:
# 若无返回值时:
def my_decorator( f):
    def wrapper(*args, **kwargs):
        print("I am doing extra stuff")
        f(*args, **kwargs)
        print("Doing more extra sturff")
    return wrapper()

# 若有返回值时:    
def my_decorator( f):
    def wrapper(*args, **kwargs):
        print("I am doing extra stuff")
        value = f(*args, **kwargs)
        print("Doing more extra sturff")
        return value
    return wrapper()
    
# *args是一个列表,包含传递给函数的所有参数

我的微信公众号

【Python】:Python概览_第1张图片

你可能感兴趣的:(【Python】:Python概览)