Python基础入门文档(一)

Python基本语法

基本类型

  • 注释:以#开头的为注释,直到行结束为止。
  • 数字:以数字,.或者-开头的为数字。带小数点.的为浮点数,不带小数点的为整数。支持科学计数法,例如52.3E-4。可以以_作为分隔,增强可读性,例如1_000_000。
  • 字符串:以单引号’,双引号”,三引号’’’开始,同样符号结束。
  • 布尔值:只有两个,True和False。大小写敏感。
  • 空值:只有一个,None。大小写敏感。表示没有值的值。

练习:写出数字和字符串的例子,并加以注释。

例子

#输入数字类型
52.3E-4
>> 0.00523
#输入字符串类型
#可用单引号
'string'
>> 'string'
#可用双引号
"string"
>>'string'
#可用三引号
'''string'''
>> 'string'
#布尔类型
True
>> True

False
>> False

1 == 0
>> False

1 == 1
>> True

#空值

字符串转义

在单引号字符串中表达单引号需要转义,双引号和三引号同。例如:

“what’s wrong”是合法字符串。’what’s wrong’会出错,需要写为’what\’s wrong’。

转义也可以用于表达不可见字符,例如tab:\t。

如果要表达\本身,也需要转义,写为\\。

'what\'s wrong'
>> "what's wrong"

Raw String

在字符串的开始加r,使得字符串中不发生转义。

常用于大量需要表达\的字符串。

r'what\'s wrong'
>> "what\\'s wrong"
print(r'what\'s wrong')
>> what\'s wrong

Unicode字符串

在字符串前加u,表示这是一个unicode字符串。前面加b,表示是一个bytes字符串(也叫raw字符串)。如果什么都不加,则看Python版本。Python2中默认为raw字符串,Python3中默认为unicode字符串。

unicode字符串和普通字符串的区别在于。unicode字符串中,每一个位表示一个“字”。而普通字符串中,一个位表示一个字节。

一个“字”可能由一个字节表示,也可能由多个字节表示。尤其是中文的情况下。这种“字符串”在处理文字的时候容易发生混乱。

unicode字符串和普通字符串外形相似,但是很多地方无法混用。

u'string'
>>  'string'
'string' == u'string'
>> True
# -*- coding: utf-8 -*-
'中文' == u'中文'
# 在Python2下不相等,Python3下相等。因为Python3下都是Unicode。
>> True

变量

变量需要一个名字。

首字母需要是字母或下划线,其余部分可以是字母,下划线和数字。

变量大小写敏感,但是使用大小写区分变量是不可取的。

变量类型

每个变量都会指向一个值,每个值都拥有一个类型。

变量在运行过程中可以指向不同的值,拥有不同的类型。

因此在运行前无法知道名字所对应的类型。(动态类型语言特征)

算符和表达式

算符

算符是连接一个或多个元素的符号,用来表达计算。

常见算符:

+ - * ** / // % << >> & | ^ ~ < > <= >= == != not and or

算符和赋值的快速写法

a = 10
a = a + 10
a += 10

可以挑讲常见算符。一般+ - * /需要重点讲解,其他算符可以简单介绍,等到合适的机会再讲。

在这里,/和//算符,2和3的差异是常被提到的话题。//是整数除法,无论参与数是浮点还是整数。

另一个讲解重点在于python不会自动转换类型。这点即是“强类型语言”。

算符优先级

  • lambda
  • if - else
  • or
  • and
  • not
  • in, not in, is, is not, <, <=, >, >=, !=, ==
  • |
  • ^
  • &
  • <<, >>
  • +, -
  • *, /, //, %
  • +x, -x, ~x
  • **
  • x[index], x[index:index], x(arguments…), x.attribute
  • (expressions…), [expressions…], {key: value…}, {expressions…}

使用()可以改变优先级顺序

同样挑讲优先级,重点几乎只有+ - * /之间。101不要全部展开。

优先级的正解应当是尽量使用(),不确定的应当用python去解决而不是大脑。

练习

计算以下表达式的求值顺序,并求值算出结果:

1 + 8 <= 1 + 2 * 3
1 + 2 and 3 * 4 or 5 ** 6

强调值的真假,and和or的运算特性。

答案

1 + 8 <= 1 + 2 * 3
>> False
1 + 2 and 3 * 4 or 5 ** 6
>> 12

表达式

表达式为一串由算符连接的元素。

表达式一般会有值,值为按照优先级依次求解表达式得到的结果。

有些表达式在语法上亦不可求值。

表达式除了值以外,还可能造成“副作用”。即造成环境改变,或执行某些操作。

Pi = 3.14
r = 5
area = Pi * r * r
print("area is {0}".format(area))
area is 78.5

格式化字符串

将变量的值格式化到字符串中,形成新的字符串。

常用于输出数据内容。

name = 'shell'

print('my name is {0} and the length of name is {1}'.format(name, len(name)))
print('my name is %s and the length of name is %d' % (name, len(name)))
my name is shell and the length of name is 5
my name is shell and the length of name is 5

f-string

借助f-string,我们可以在字符串内直接引用变量,格式化字符串。

name = 'shell'

print(f'my name is {name} and the length of name is {len(name)}')
my name is shell and the length of name is 5

执行流控制

执行流控制代码控制流的先后执行顺序。

逻辑行和物理行

Python语句执行以行为单位。每行可选的可以以分号;结尾。

但是物理上的一行可以包含多个执行单位,例如:

i = 5; print(i);
>> 5

同一个执行单位也可以拆分为多行,例如:

print(
'Hello, world')
>> Hello, world

格式对齐

Python使用对齐表达逻辑,例如:

a = 1
if a == 1:
    print('ok')
print('done')
ok
done

当print(‘done’)和if对齐时,必然被执行。而和print(‘ok’)对齐时,在条件下执行。

因此对齐不合适的代码会引发语法错误。

a = 0
if a == 1:
    print('ok')
print('done')

print('------')

a = 1
if a == 1:
    print('ok')
print('done')
done
------
ok
done
a = 0
if a == 1:
    print('ok')
    print('done')

print('------')

a = 1
if a == 1:
    print('ok')
    print('done')
------
ok
done

单行写法

if True: print('ok')
>> ok

if

import random
# input是python3写法,raw_input是对应的python2写法。
# 此处四行是Python2/3兼容写法,后续统一写为input,不再出现这四行。
try:
    input = raw_input
except NameError:
    pass

number = random.randint(10, 30)
print('number:', number)

guess = random.randint(10, 30)
print("guess:", guess)
if guess == number:
    print('Congratulations, you guessed it.')
    print('(but you do not win any prizes!)')
elif guess < number:
    print('No, it is a little higher than that')
else:
    print('No, it is a little lower than that')
print('Done')
number: 12
guess: 23
No, it is a little lower than that
Done

while

import random

number = random.randint(0, 99)
print('number:', number)

low = 0
high = 100
running = True

while running:
    guess = int((low+high)/2)
    print('guess:', guess)
    if guess == number:
        print('Congratulations, you guessed it.')
        running = False
    elif guess < number:
        print('No, it is a little higher than that.')
        low = guess
    else:
        print('No, it is a little lower than that.')
        high = guess
else:
    print('The while loop is over.')
print('Done')
number: 61
guess: 50
No, it is a little higher than that.
guess: 75
No, it is a little lower than that.
guess: 62
No, it is a little lower than that.
guess: 56
No, it is a little higher than that.
guess: 59
No, it is a little higher than that.
guess: 60
No, it is a little higher than that.
guess: 61
Congratulations, you guessed it.
The while loop is over.
Done

for

for i in range(1, 5):
    print(i)
else:
    print('The for loop is over')
1
2
3
4
The for loop is over

break

import random

number = random.randint(0, 99)
print('number:', number)

low = 0
high = 100
running = True

while running:
    guess = int((low+high)/2)
    print('guess:', guess)
    if guess == number:
        print('Congratulations, you guessed it.')
        break
    elif guess < number:
        print('No, it is a little higher than that.')
        low = guess
    else:
        print('No, it is a little lower than that.')
        high = guess
else:
    print('The while loop is over.')
print('Done')
number: 90
guess: 50
No, it is a little higher than that.
guess: 75
No, it is a little higher than that.
guess: 87
No, it is a little higher than that.
guess: 93
No, it is a little lower than that.
guess: 90
Congratulations, you guessed it.
Done

continue

import random

number = random.randint(0, 99)
print('number:', number)

low = 0
high = 100
running = True

while running:
    guess = int((low+high)/2)
    print('guess:', guess)
    if guess == number:
        print('Congratulations, you guessed it.')
        break
    if guess < number:
        print('No, it is a little higher than that.')
        low = guess
        continue
    if guess > number:
        print('No, it is a little lower than that.')
        high = guess
        continue
else:
    print('The while loop is over.')
print('Done')
number: 84
guess: 50
No, it is a little higher than that.
guess: 75
No, it is a little higher than that.
guess: 87
No, it is a little lower than that.
guess: 81
No, it is a little higher than that.
guess: 84
Congratulations, you guessed it.
Done

练习

说出下面代码的执行过程和输出结果:

for i in range(12):
    if i < 5:
        continue
    elif i > 9:
        break
    else:
        print(i)

答案

for i in range(12):
    if i < 5:
        continue
    elif i > 9:
        break
    else:
        print(i)
5
6
7
8
9

函数

函数的定义和调用

定义:

def say_hello():
    print('hello world')

调用:

say_hello()
>> hello world

参数

可以通过参数向函数传递数据。

def print_max(a, b):
    if a > b:
        print('{0} is maximum'.format(a))
    elif a == b:
        print('{0} is equal to {1}'.format(a, b))
    else:
        print('{0} is maximum'.format(b))
print_max(3, 4)
4 is maximum
# python2.7下通过,python3下报错
try:
    print_max(3, '4')
    print_max('3', 4)
except:
    print('exceptions')
exceptions

坑爹。print_max(3, ‘4’)居然也能执行。说好的强类型呢?

需要着重声明例子中的返回是违背常理的。

局部变量

在函数体内定义的变量称为局部变量。

局部变量在函数执行时产生,在函数结束时消亡。函数外无法看到局部变量。

参数可视为一种局部变量。

对局部变量的修改不影响全局变量。

x = 50
def func():
    x = 2
    print('Changed local x to {0}'.format(x))
func()
print(x)
Changed local x to 2
50
x = 50
def func(x):
    print('Changed local x to {0}'.format(x))
func(2)
print(x)
Changed local x to 2
50

全局变量

在模块内定义的变量叫全局变量。

全局变量在全局可见,函数体内可以引用全局变量。

函数可以用global关键字声明某变量是全局的。

x = 50
def func():
    print('x is {0}'.format(x))
func()
x is 50
x = 50
def func():
    global x
    print('x is {0}'.format(x))
    x = 2
    print('Changed x to {0}'.format(x))
    
print('x is {0} before function.'.format(x))
func()
print('x is {0} after function.'.format(x))
x is 50 before function.
x is 50
Changed x to 2
x is 2 after function.

作用域

变量可见的范围叫做变量的作用域。

局部变量的作用域在函数内,全局变量的作用域在模块内(关于模块后文会讲)。

作用域的基本原则是,内层作用域可访问外层作用域的变量,反之外层不可访问内层变量。

如果两个作用域内有同一个名字,那么内层的起作用。

Unbound异常

python内的“local中有变量定义”,是以local中存在对变量构建的语法(例如赋值)为标准的。

这个构建语法所在行可能尚未执行(或永远无法执行到),但是这并不影响“local中有变量定义”这个事实。

当local中定义了变量,但是变量尚未完成构建时,对变量做引用,会发生什么?

x = 1

def func():
    print(x)
    x = 2
    
func()
---------------------------------------------------------------------------

UnboundLocalError                         Traceback (most recent call last)

<ipython-input-47-d73f257fa89f> in <module>()
      5     x = 2
      6 
----> 7 func()


<ipython-input-47-d73f257fa89f> in func()
      2 
      3 def func():
----> 4     print(x)
      5     x = 2
      6 


UnboundLocalError: local variable 'x' referenced before assignment

参数默认值

函数定义时,可以给参数一个值。当调用时,该参数并未给出的情况下,使用默认值。

提示:从安全性角度来说,不要使用可变对象作为参数默认值。

def say(message, times=1):
    print(message * times)

say('Hello')
say('World', 5)
Hello
WorldWorldWorldWorldWorld
def append1(l=[]):
    l.append(1)
    return l

print(append1([]))
print(append1([]))

print('------')

print(append1())
print(append1())
[1]
[1]
------
[1]
[1, 1]

遇到希望使用可变对象作为参数默认值的情况,需要使用None作为默认值。

在函数中判断是否为None,是的话再对参数做默认值赋值处理。

以此模式来避免上述问题。

def append1(l=None):
    if l is None:
        l = []
    l.append(1)
    return l

print(append1([]))
print(append1([]))

print('------')

print(append1())
print(append1())
[1]
[1]
------
[1]
[1]

返回值

将函数内数据传递回去。

def maximum(x, y):
    if x > y:
        return x
    elif x == y:
        return 'The numbers are equal'
    else:
        return y

print(maximum(2, 3))
print(maximum(2, 2))
3
The numbers are equal

多值返回

Python允许一次返回多个值。

多值返回的本质是使用tuple中转(后面详细讲解)。

提前强调“最小惊讶原则”。

def f():
    return 1,2,3,4,5
print (f())

a,b,c,d,e = f()
print(c)
(1, 2, 3, 4, 5)
3

Doc String

Python允许为每个函数增加一个文档字符串,这个文档字符串可以被代码访问。

具体方式是,在函数的第一个逻辑行定义一个字符串(不执行任何操作)。

访问方式是function.__doc__。

讲解doc string在文档里的作用,对比java doc。

def print_max(x, y):
    '''Prints the maximum of two numbers. \nThe two values must be integers.'''
    x = int(x)
    y = int(y)
    if x > y:
        print('{} is maximum'.format(x))
    else:
        print('{} is maximum'.format(y))

print_max(3, 5)
print(print_max.__doc__)
5 is maximum
Prints the maximum of two numbers. 
The two values must be integers.

Annotations

Python允许对函数的变量进行注解,表明参数和返回值的类型。

def maximum(x: int, y: int) -> int:
    if x > y:
        return x
    elif x == y:
        return 'The numbers are equal'
    else:
        return y

maximum(1, 2)
2

然而,Python默认对注解不做验证。

maximum('1', '2')
'2'

lambda

一种快速定义简单函数的方法。

def inc(n):
    return n+1
print(inc(10))

inc = lambda n: n+1
print(inc(10))
11
11

练习

定义fib函数。

输入n,fib的输出为数列第n项的值。

数列某项等于前两项之和。

可演示递归和循环算法,比较其特性。说明简洁和快速的矛盾。

可以在最后使用for循环打印输出,为后面“__main__标准写法”打基础。

答案

def fib(n):
    if n <= 1: return 1
    return fib(n-1) + fib(n-2)

fib(10)
89

模块

Module

预先写好的代码,供其他代码使用。

一个module是一个独立文件。

每个module拥有自己的全局变量空间。数据定义,函数,均定义在其中。

%%writefile testmod.py
varible1 = 1
varible2 = 2

def add(a, b):
    return a+b

if __name__ == '__main__':
    print('run as script')
Writing testmod.py
!ls -l testmod.py
-rw-r--r-- 1 shell shell 111 Mar 22 20:06 testmod.py

Windows下要用dir testmod.py

import

import引入某个module对象,可使用module.name的方式引用它的全局变量。

import testmod
testmod.varible1
1

from import

from import引入模块中的某个(或全部)变量。

被引入变量不需要module.name来访问,仅需要name。

注意:不推荐引入全部变量,会引起名称空间污染。

from testmod import varible2
varible2
2

dir

列出某个模块的所有变量。

import testmod
dir(testmod)
['__builtins__',
 '__cached__',
 '__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'add',
 'varible1',
 'varible2']

模块预编译

当import时,python会试图去编译出pyc文件来。

pyc是被编译过的py文件,加载pyc文件可以跳过语法解析过程。

当py日期新于pyc时,重新生成pyc。所以日期紊乱可能导致执行老代码。

在Python3(3.2以后)中,会在当前目录下生成__pycache__目录,来缓存pyc文件。

这样可以避免多个Python解释器无法互相载入对方的pyc文件。

具体可以参考这里: https://docs.python.org/3/whatsnew/3.2.html

注意加载顺序导致当前目录模块名和系统相同时会发生覆盖问题。

同时注意这个现象受制于系统库是否已经载入,从而引出系统库不会多次“加载”的结论。

!ls -l testmod*
-rw-r--r-- 1 shell shell 111 Mar 22 20:06 testmod.py
!ls -l __pycache__/testmod*
-rw-r--r-- 1 shell shell 365 Jun 25  2018 __pycache__/testmod.cpython-35.pyc
-rw-r--r-- 1 shell shell 349 Mar 22 20:06 __pycache__/testmod.cpython-37.pyc

由于这个系统使用了Python3作为基础,因此没有testmod.pyc文件。

__name__属性

模块有一个属性,__name__。当这个属性为’__main__'时,说明当前模块被作为脚本运行。

模块被作为以脚本运行时,不生成pyc文件(因为不是import)。

__name__
>>  '__main__'
testmod.__name__
>> 'testmod'

Main文件模式化写法

!tail -n 2 testmod.py
if __name__ == '__main__':
    print('run as script')
!python testmod.py
>>  run as script
!rm testmod*

Package

从组织结构上说,package是比modules更大一级的结构。

一个package里可以包含多个modules和packages。

一般一个package是一个独立目录,里面有__init__.py文件。

这个文件指明了如何加载整个package。

可以找一些实际的文件布局的例子。

练习

  • 导入系统sys模块
  • 列出sys模块中以s开头并且以e结尾的成员。

此处需要提前说明startswith和endswith的用法。

答案

import sys
for name in dir(sys):
    if name.startswith('s') and name.endswith('e'):
        print(name)
setprofile
settrace

内置数据结构

  • list
  • tuple
  • dict
  • set
  • string

list

具有顺序的一组对象。

可以随意访问其中的任意位置。

可以添加和删除。

其中的元素不需要是同类型。

原书例子有问题,那是python3的代码。

import sys
shoplist = ['apple', 'mango', 'carrot', 'banana']

print('I have {} items to purchase.'.format(len(shoplist)))
sys.stdout.write('These items are: ')
for item in shoplist:
    sys.stdout.write(str(item) + ' ')

print('\nI also have to buy rice.')
shoplist.append('rice')
print('My shopping list is now {0}'.format(shoplist))

print('I will sort my list now')
shoplist.sort()
print('Sorted shopping list is {0}'.format(shoplist))

print('The first item I will buy is {0}'.format(shoplist[0]))
olditem = shoplist[0]
del shoplist[0]
print('I bought the {0}'.format(olditem))
print('My shopping list is now {0}'.format(shoplist))
I have 4 items to purchase.
These items are: apple mango carrot banana 
I also have to buy rice.
My shopping list is now ['apple', 'mango', 'carrot', 'banana', 'rice']
I will sort my list now
Sorted shopping list is ['apple', 'banana', 'carrot', 'mango', 'rice']
The first item I will buy is apple
I bought the apple
My shopping list is now ['banana', 'carrot', 'mango', 'rice']

tuple

具有顺序的一组对象。

可以随意访问其中的任意位置。

不可以添加和删除。

其中的元素不需要是同类型。

zoo = ('python', 'elephant', 'penguin')
print('Number of animals in the zoo is {0}'.format(len(zoo)))

new_zoo = 'monkey', 'camel', zoo
print('Number of cages in the new zoo is {0}'.format(len(new_zoo)))

print('All animals in new zoo are {0}'.format(new_zoo))
print('Animals brought from old zoo are {0}'.format(new_zoo[2]))
print('Last animal brought from old zoo is {0}'.format(new_zoo[2][2]))
print('Number of animals in the new zoo is {0}'.format(len(new_zoo)-1+len(new_zoo[2])))
Number of animals in the zoo is 3
Number of cages in the new zoo is 3
All animals in new zoo are ('monkey', 'camel', ('python', 'elephant', 'penguin'))
Animals brought from old zoo are ('python', 'elephant', 'penguin')
Last animal brought from old zoo is penguin
Number of animals in the new zoo is 5
r = 1, 2, 3
r
(1, 2, 3)
a, b, c = r
print(a)
print(b)
print(c)
1
2
3
a, b = b, a
print(a)
print(b)
2
1

操作序列对象

  • 通过[]访问
  • 通过[]设定值
  • 通过for枚举
  • 通过[:]访问
  • 通过[:]设定值
  • 通过[::]设定步进
shoplist = ['apple', 'mango', 'carrot', 'banana']
name = 'swaroop'
print('Item 0 is {0}'.format(shoplist[0]))
print('Item 1 is {0}'.format(shoplist[1]))
print('Item 2 is {0}'.format(shoplist[2]))
print('Item 3 is {0}'.format(shoplist[3]))
print('Item -1 is {0}'.format(shoplist[-1]))
print('Item -2 is {0}'.format(shoplist[-2]))
print('Character 0 is {0}'.format(name[0]))

print('Item 1 to 3 is {0}'.format(shoplist[1:3]))
print('Item 2 to end is {0}'.format(shoplist[2:]))
print('Item 1 to -1 is {0}'.format(shoplist[1:-1]))
print('Item start to end is {0}'.format(shoplist[:]))
print('characters 1 to 3 is {0}'.format(name[1:3]))
print('characters 2 to end is {0}'.format(name[2:]))
print('characters 1 to -1 is {0}'.format(name[1:-1]))
print('characters start to end is {0}'.format(name[:]))
print('reversed characters is {0}'.format(name[::-1]))
Item 0 is apple
Item 1 is mango
Item 2 is carrot
Item 3 is banana
Item -1 is banana
Item -2 is carrot
Character 0 is s
Item 1 to 3 is ['mango', 'carrot']
Item 2 to end is ['carrot', 'banana']
Item 1 to -1 is ['mango', 'carrot']
Item start to end is ['apple', 'mango', 'carrot', 'banana']
characters 1 to 3 is wa
characters 2 to end is aroop
characters 1 to -1 is waroo
characters start to end is swaroop
reversed characters is pooraws

练习

  1. 建立一个list,内容为10以内的所有质数。
  2. 计算出100内所有质数的列表。
  3. 将这个列表转为tuple。
  4. 根据上面的tuple,获得反转的tuple。
  5. 将一个list分为两个:第一个list拥有原本list前一半的反向,第二个list拥有原本list后一半的反向。

埃拉托斯特尼筛法。注意代码实现中的筛法效率是有问题的。主要因为sqrt函数没教过。

答案

primes = [2, 3, 5, 7]
primes
[2, 3, 5, 7]
for i in range(10, 100):
    prime = True
    for j in primes:
        if i % j == 0:
            prime = False
            break
    if prime:
        primes.append(i)
print(primes)
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
tuple_primes = tuple(primes)
print(tuple_primes)
(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97)
print(tuple_primes[::-1])
(97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2)
print(tuple(reversed(tuple_primes)))
(97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2)
# Python2下可以不需要int,因为len结果是整数,整数除法结果是整数。
# 在Python3,没有int会导致出错。
list(reversed(primes[:int(len(primes)/2)])), list(reversed(primes[int(len(primes)/2):]))
([37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2],
 [97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41])
primes[int(len(primes)/2-1)::-1], primes[:int(len(primes)/2-1):-1]
([37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2],
 [97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41])

列表推导式

l = range(12)
t = [i * i for i in l]
print(t)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121]
t = [i * i for i in l if i % 2 != 0]
print(t)
[1, 9, 25, 49, 81, 121]

练习

思考一下,列表推导式是否能够将素数列表直接实现出来?为什么?

然后自己测试一下。

问题的关键在于“生成列表的时候列表是空的”。所以不能一次性生成,必须渐进。

dict

具有一系列成对对象。一个叫做key,一个叫做value。

无顺序,key不可重复。

通过其中一个(key)可以唯一确定另外一个(value),反之未必。

可以添加和删除。

其中的元素(包括key和value)不需要是同类型。

key必须是不可变对象。

1: 注意在某些情况下,dict可表现出内在顺序。但这并不是有序约定。

2: 在Python3.6之后,dict表现出类似于OrderedDict的性质。现阶段可无视这个差异。

ab = {
    'Swaroop': 'swaroop@swaroopch.com',
    'Larry': 'larry@wall.org',
    'Matsumoto': 'matz@ruby-lang.org',
    'Spammer': 'spammer@hotmail.com'
}

print("Swaroop's address is {}".format(ab['Swaroop']))
del ab['Spammer']

print('There are {} contacts in the address-book'.format(len(ab)))
print('Name list: %s' % ', '.join(ab))
for name, address in ab.items():
    print('Contact {} at {}'.format(name, address))

ab['Guido'] = 'guido@python.org'
if 'Guido' in ab:
    print("Guido's address is {}".format(ab['Guido']))
Swaroop's address is swaroop@swaroopch.com
There are 3 contacts in the address-book
Name list: Swaroop, Larry, Matsumoto
Contact Swaroop at swaroop@swaroopch.com
Contact Larry at larry@wall.org
Contact Matsumoto at matz@ruby-lang.org
Guido's address is guido@python.org

set

具有一组对象。无顺序,不可重复。

可以添加和删除。

其中的元素不需要是同类型,但必须是不可变对象。

bri = set(['brazil', 'russia', 'india'])
bri = {'brazil', 'russia', 'india'}
'india' in bri
>> True
'usa' in bri
>> False
bric = bri.copy()
bric.add('china')
print(bric)
bric.issuperset(bri)
{'china', 'russia', 'brazil', 'india'}





True
bric.remove('russia')
print(bric)
bri & bric
{'china', 'brazil', 'india'}





{'brazil', 'india'}
bri | bric
>>  {'brazil', 'china', 'india', 'russia'}

string的一些其他方法

  • startswith
  • endswith
  • in
  • find
  • replace
  • upper
  • lower
  • join
  • split
name = 'Swaroop'
if name.startswith('Swa'):
    print('Yes, the string starts with "Swa"')

if 'a' in name:
    print('Yes, it contains the string "a"')

pos = name.find('war')
if pos != -1:
    print('position of the string "war" is %d' % pos)

print("the string replace war to peace is %s" % name.replace('war', 'peace'))
print("the string replace o to 0 is %s" % name.replace('o', '0'))

print("upper case: %s" % name.upper())
print("lower case: %s" % name.lower())

delimiter = '_*_'
mylist = ['Brazil', 'Russia', 'India', 'China']
print(delimiter.join(mylist))

print(delimiter.join(mylist).split(delimiter))
print(delimiter.join(mylist).split('*'))
Yes, the string starts with "Swa"
Yes, it contains the string "a"
position of the string "war" is 1
the string replace war to peace is Speaceoop
the string replace o to 0 is Swar00p
upper case: SWAROOP
lower case: swaroop
Brazil_*_Russia_*_India_*_China
['Brazil', 'Russia', 'India', 'China']
['Brazil_', '_Russia_', '_India_', '_China']

练习

每个国家都有一定的地区号码,现在我们知道这些国家和地区号码关系:

+30 希腊 +45 丹麦 +51 秘鲁 +65 新加坡 +86 中国

写代码求出以下电话号码里面有几个国家:

+86 1123 +65 1234 +51 2347 +30 9123 +65 1246 +30 2347 +86 2935

答案

number2country = {
    '+30': u'希腊',
    '+45': u'丹麦',
    '+51': u'秘鲁',
    '+65': u'新加坡',
    '+86': u'中国'
}

numbers = "+86 1123 +65 1234 +51 2347 +30 9123 +65 1246 +30 2347 +86 2935".split()
country_set = set([number2country[n] for n in numbers if n.startswith('+')])

print(len(country_set))
# 注意:这里的输出顺序在不同的机器上可能不一样,具体依赖于python的版本和实现。set本身是没有顺序约定的。
print(', '.join(country_set))
4
希腊, 秘鲁, 新加坡, 中国
numbers = "+86 1123 +65 1234 +51 2347 +30 9123 +65 1246 +30 2347 +86 2935".split()
print("numbers: %s" % str(numbers))
print("filtered: %s" % str([n for n in numbers if n.startswith('+')]))
print("filtered and mapped: %s" % ', '.join([number2country[n] for n in numbers if n.startswith('+')]))
country_set = set([number2country[n] for n in numbers if n.startswith('+')])
print(', '.join(country_set))
numbers: ['+86', '1123', '+65', '1234', '+51', '2347', '+30', '9123', '+65', '1246', '+30', '2347', '+86', '2935']
filtered: ['+86', '+65', '+51', '+30', '+65', '+30', '+86']
filtered and mapped: 中国, 新加坡, 秘鲁, 希腊, 新加坡, 希腊, 中国
希腊, 秘鲁, 新加坡, 中国

面对对象编程

“一个苹果加一个苹果是什么?”

“两个苹果。”

“一个雪梨加一个雪梨呢?”

“两个雪梨。”

“一个苹果加一个雪梨呢?”

“……”

虽然妲柯妮丝不知道这和欣赏抽象画有什么关系,但还是敲着额头想了想。

“……两个水果?”

“明白了吧?”冰清灵看着若有所思的妲柯妮丝笑道,“从苹果、雪梨到水果,其实是一个去掉不同的部分,然后把相同的部分进行累加的一个过程。从水果、杯子到‘东西’,也是它们的存在本身,也是一样的道理。”

——XINPINGYE《御天者.星星》

class

如何定义类:

class Person:
    pass  # An empty block

如何使用类产生对象:

p = Person()
print(p)
<__main__.Person object at 0x7f13cc23b358>

方法

如何定义类的成员方法:

class Person:
    def say_hi(self):
        print('Hello, how are you?')

p = Person()
p.say_hi()
Hello, how are you?

数据

对象里包含的数据。

可以用.访问。

和方法的主要差别在于。方法需要使用()来调用,而数据不需要。

class Person:
    def set_name(self, name):
        self.name = name

    def say_hi(self):
        print('hello, {}, how are you?'.format(self.name))
        
p = Person()
p.set_name('shell')
print(p.name)
p.say_hi()
shell
hello, shell, how are you?

self

在python中,成员函数使用”self”来指代对象自身。类似于java和C++中的”this”。

在调用时,p.say_hi()即可。不需要传递p.say_hi(self)或者p.say_hi§。

在使用时,需要先将self定义为第一个参数。例如def say_hi(self):。

在函数内使用时,对象的成员需要用self.xx的方式使用,例如self.name。

self不是关键词,也不是强制名称。它只是函数的第一个参数。但是为了保持传统,请不要将他改为其他名称。

__init__方法

__init__是对象的第一个方法,用于初始化对象数据。

__init__函数是初始化方法,而非构造方法。

在__init__函数被调用时,self对象已经创建完毕。

在python中,对象的属性可以自由添加删除,不需要先在类里面声明。

一般而言,对象里的所有数据会在__init__方法中赋值,后面可以变更。

但是不在__init__中赋值,后面直接赋值创建属性也是合法的。

class Person:
    def __init__(self, name):
        self.name = name

    def set_name(self, name):
        self.name = name

    def say_hi(self):
        print('hello, {}, how are you?'.format(self.name))

p = Person('Swaroop')
print(p.name)
p.say_hi()

p.set_name('shell')
print(p.name)
p.say_hi()
Swaroop
hello, Swaroop, how are you?
shell
hello, shell, how are you?

练习

定义一个马克杯类,定义一个加水方法和一个喝水方法。

答案

class Mug:

    def __init__(self):
        self.water = 0

    def drink(self, mass):
        self.water -= mass

    def watering(self, mass):
        self.water += mass

类成员和对象成员

类和对象分别拥有成员,例如数据和方法。

对象可以引用类成员,例如p.say_hi()。也可以引用对象成员,或者self.name。

当有重名时优先引用对象成员。

类成员在所有对象间共享,而对象成员只是它自己的。

这里是否要加个例子说明类和对象变量的优先引用次序。

class Robot:
    """Represents a robot, with a name."""

    # A class variable, counting the number of robots
    population = 0

    def __init__(self, name):
        """Initializes the data."""
        self.name = name
        print("(Initializing {})".format(self.name))
        # When this person is created, the robot
        # adds to the population
        Robot.population += 1

    def die(self):
        """I am dying."""
        print("{} is being destroyed!".format(self.name))
        Robot.population -= 1
        if Robot.population == 0:
            print("{} was the last one.".format(self.name))
        else:
            print("There are still {:d} robots working.".format(
                Robot.population))

    def say_hi(self):
        """Greeting by the robot.
        Yeah, they can do that."""
        print("Greetings, my masters call me {}.".format(self.name))

    @classmethod
    def how_many(cls):
        """Prints the current population."""
        print("We have {:d} robots.".format(cls.population))
droid1 = Robot("R2-D2")
droid1.say_hi()
Robot.how_many()

droid2 = Robot("C-3PO")
droid2.say_hi()
Robot.how_many()

print("\nRobots can do some work here.\n")

print("Robots have finished their work. So let's destroy them.")
droid1.die()
droid2.die()

Robot.how_many()
(Initializing R2-D2)
Greetings, my masters call me R2-D2.
We have 1 robots.
(Initializing C-3PO)
Greetings, my masters call me C-3PO.
We have 2 robots.

Robots can do some work here.

Robots have finished their work. So let's destroy them.
R2-D2 is being destroyed!
There are still 1 robots working.
C-3PO is being destroyed!
C-3PO was the last one.
We have 0 robots.

练习

回想一下,上一节你的定义里是否考虑了马克杯的“最大容量”。如果没有,请加上。

思考一下,“最大容量”是对象属性还是类属性,为什么?

答案

class Mug:

    capacity = 300

    def __init__(self):
        self.water = 0

    def drink(self, mass):
        self.water -= mass
        if self.water < 0:
            self.water = 0

    def watering(self, mass):
        self.water += mass
        if self.water > self.capacity:
            self.water = self.capacity

继承

继承关系分为基类和继承类(也叫父类和子类)。

子类可以继承父类的成员。当子类和父类定义同名成员时,优先引用子类的。

原则上说,父类能做的事,子类一定能做(虽然行为可能有差异)。父类能出现的地方,子类一定能出现。

因为子类拥有父类的所有成员。

继承的写法为:

class Person(Animal):
    pass
class SchoolMember:
    '''Represents any school member.'''
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('(Initialized SchoolMember: {})'.format(self.name))

    def tell(self):
        '''Tell my details.'''
        print('Name:"{}" Age:"{}"'.format(self.name, self.age))


class Teacher(SchoolMember):
    '''Represents a teacher.'''
    def __init__(self, name, age, salary):
        SchoolMember.__init__(self, name, age)
        self.salary = salary
        print('(Initialized Teacher: {})'.format(self.name))

    def tell(self):
        SchoolMember.tell(self)
        print('Salary: "{:d}"'.format(self.salary))
class Student(SchoolMember):
    '''Represents a student.'''
    def __init__(self, name, age, marks):
        SchoolMember.__init__(self, name, age)
        self.marks = marks
        print('(Initialized Student: {})'.format(self.name))

    def tell(self):
        SchoolMember.tell(self)
        print('Marks: "{:d}"'.format(self.marks))

t = Teacher('Mrs. Shrividya', 40, 30000)
s = Student('Swaroop', 25, 75)

# prints a blank line
print()

members = [t, s]
for member in members:
    # Works for both Teachers and Students
    member.tell()
(Initialized SchoolMember: Mrs. Shrividya)
(Initialized Teacher: Mrs. Shrividya)
(Initialized SchoolMember: Swaroop)
(Initialized Student: Swaroop)

Name:"Mrs. Shrividya" Age:"40"
Salary: "30000"
Name:"Swaroop" Age:"25"
Marks: "75"

练习

请为马克杯增加一个“添满”方法,可以求出需要添多少水到加满,并自动为杯子加水。

答案

class Mug(object):
    capacity = 300

    def __init__(self):
        self.water = 0

    def drink(self, mass):
        self.water -= mass
        if self.water < 0:
            self.water = 0

    def watering(self, mass):
        self.water += mass
        if self.water > self.capacity:
            self.water = self.capacity

    def full(self):
        mass = self.capacity - self.water
        self.watering(mass)

练习

请定义一个运动杯类,并同样实现“喝水”,“加水”和“添满”方法。

练习

考虑一下,如果“马克杯”和“运动杯”的“喝水”和“添水”行为是不一样的。

那么,“添满”行为是否一样,两边是否可以重用“添满”?

如果可以,怎么做?

答案

class Cup(object):

    def __init__(self):
        self.water = 0

    def full(self):
        mass = self.capacity - self.water
        self.watering(mass)

class Mug(Cup):
    capacity = 300

    def drink(self, mass):
        self.water -= mass
        if self.water < 0:
            self.water = 0

    def watering(self, mass):
        self.water += mass
        if self.water > self.capacity:
            self.water = self.capacity

class SportBottle(Cup):
    capacity = 500

    def drink(self, mass):
        self.water -= mass
        if self.water < 0:
            self.water = 0

    def watering(self, mass):
        self.water += mass
        if self.water > self.capacity:
            self.water = self.capacity
m = Mug()
m.full()
m.drink(100)
print(m.water)

s = SportBottle()
s.full()
s.drink(100)
print(s.water)
200
400

输入输出

从命令行输入

Python3使用input从命令行输入。Python2使用raw_input从命令行输入。

两者参数一致,都是一个提示字符串。

在Python2中,input是eval(input),有安全性问题。

#Rise to vote, sir.

def reverse(text):
    return text[::-1]

def is_palindrome(text):
    return text == reverse(text)

something = input("Enter text: ")

if is_palindrome(something):
    print("Yes, it is a palindrome")
else:
    print("No, it is not a palindrome")
Enter text: abc
No, it is not a palindrome

练习

刚刚的回文字符串程序只能检测纯粹的字符串,真正的回文字符检测程序应当能够处理标点,空格和大小写。

例如"Rise to vote, sir."实际上也是回文字符串,但是刚刚的程序判定他不是。

请写出一个新的版本。

简单起见,只需要处理,和.即可。先不用管理各种复杂标点,例如&。

答案

def reverse(text):
    return text[::-1]

def is_palindrome(text):
    text = text.replace(',', '')
    text = text.replace('.', '')
    text = text.replace(' ', '')
    text = text.lower()
    return text == reverse(text)

something = input("Enter text: ")

if is_palindrome(something):
    print("Yes, it is a palindrome")
else:
    print("No, it is not a palindrome")
Enter text: Rise to vote, sir.
Yes, it is a palindrome

从文件中输入输出

使用open打开文件进行读写。调用方法和C的fopen类似。

import sys
poem = '''\
Programming is fun
When the work is done
if you wanna make your work also fun:
    use Python!
'''
f = open('poem.txt', 'w')
f.write(poem)
f.close()

f = open('poem.txt')
while True:
    line = f.readline()
    if len(line) == 0:
        break
    sys.stdout.write(line)
f.close()
Programming is fun
When the work is done
if you wanna make your work also fun:
    use Python!
!ls -l poem.txt
-rw-r--r-- 1 shell shell 95 Mar 22 20:06 poem.txt
!cat poem.txt
Programming is fun
When the work is done
if you wanna make your work also fun:
    use Python!

以上皆是linux指令,因此不能(也不需要)在windows上执行。windows上对应的行为为dir,type,和del。

异常

简介

Python允许在出现错误的时候,“抛”出这个错误。

错误按照调用顺序依次向上找,找到第一个合适的处理方法对错误进行处理。如果无人处理错误,则程序崩溃。

这种错误处理机制允许调用者对函数深层的错误进行容错,同时中间所有代码对这个过程无需干预。

>>> Print("Hello World")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'Print' is not defined
>>> print("Hello World")
Hello World
Print("Hello World")
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

<ipython-input-114-82ee0b7fee85> in <module>()
----> 1 Print("Hello World")


NameError: name 'Print' is not defined
print("Hello World")
Hello World

异常输出阅读

python的大多数错误会伴随抛出异常。因此,为了解决日常在使用中碰到的种种问题,我们需要学会如何阅读异常。

异常输出通常和函数的调用顺序相同,和栈的顺序相反。

最上层的调用(最先发生的)在最上面,最后执行到的地方在最下面。最后一个异常行就是异常发生的精确地点。

最后一行是异常的名字和参数。

def layer1():
    print('layer1 called')
    return layer2()

def layer2():
    print('layer2 called')
    raise Exception('arg1', 2)
    return 1

print(layer1())
layer1 called
layer2 called



---------------------------------------------------------------------------

Exception                                 Traceback (most recent call last)

<ipython-input-116-ee9e317c1a37> in <module>()
      8     return 1
      9 
---> 10 print(layer1())


<ipython-input-116-ee9e317c1a37> in layer1()
      1 def layer1():
      2     print('layer1 called')
----> 3     return layer2()
      4 
      5 def layer2():


<ipython-input-116-ee9e317c1a37> in layer2()
      5 def layer2():
      6     print('layer2 called')
----> 7     raise Exception('arg1', 2)
      8     return 1
      9 


Exception: ('arg1', 2)

异常处理

常规异常处理代码段需要包含三部分内容:被监管代码体,错误类型和异常处理代码体。

当被监管代码体中发生异常时,异常被向上抛出。

向上寻找处理函数的异常属于定义的这个错误类的子类时,异常处理代码体被调用。

一般来说,除非明确知道确实要捕获所有异常,否则严禁用捕获所有异常来处理某种特定问题。

try:
    number = int(input('Enter something --> '))
except ValueError:
    print('Illegal number')
else:
    print(number+1)
Enter something --> abc
Illegal number

常见异常

  • AttributeError
  • ImportError
  • IndexError
  • KeyError
  • NameError
  • SyntaxError
  • IndentationError
  • TypeError
  • ValueError

抛出异常

def f():
    print("I'm ok")
    raise Exception('actually not')

try:
    f()
except Exception as err:
    print(err)
I'm ok
actually not

自定义异常

# 必须自Exception中继承
class ShortInputException(Exception):
    '''A user-defined exception class.'''
    def __init__(self, length, atleast):
        Exception.__init__(self)
        self.length = length
        self.atleast = atleast
try:
    text = input('Enter something --> ')
    if len(text) < 3:
        raise ShortInputException(len(text), 3)
except ShortInputException as ex:
    print('ShortInputException: The input was {0} long, expected at least {1}'.format(
        ex.length, ex.atleast))
else:
    print('No exception was raised.')
Enter something --> a
ShortInputException: The input was 1 long, expected at least 3

finally

finally用于“无论是异常还是正常,以下内容必然被执行”的情况。

多用于清理。

try:
    number = int(input('Enter something --> '))
except ValueError:
    print('Illegal number')
else:
    print(number+1)
finally:
    print('(clean up here)')
Enter something --> abc
Illegal number
(clean up here)

with

with可以在推出区块时自动关闭文件,而且对异常安全(异常在下一节讲)。

with不仅可以用于文件,而且可以用于很多需要在离开区域时自动关闭的对象,例如锁。

import sys
with open("poem.txt") as f:
    for line in f:
        sys.stdout.write(line)
Programming is fun
When the work is done
if you wanna make your work also fun:
    use Python!
!rm -f poem.txt

assert

assert 1 == 0, Exception('of course')
---------------------------------------------------------------------------

AssertionError                            Traceback (most recent call last)

<ipython-input-124-edbb3195c18c> in <module>()
----> 1 assert 1 == 0, Exception('of course')


AssertionError: of course

文档

文档获得和查阅

在线文档:

https://docs.python.org/3/

本地文档:随安装版本变化。

两者冲突以本地为准,本地一定对应安装使用的版本。

库查Library Reference,这是最主要部分。

语法特性查Language Reference。

练习

打开文档,请查阅itertools.permutations的意义,参数,返回值,用法,注意要点等信息。并向大家解释。

第三方文档查询

没有什么固定方法。

在google(注意,不是baidu)上搜索关键词。找一个比较像的官网。找到文档。

可以参考pypi,很多第三方库可以在上面找到。里面往往带有文档地址。

缺点是,pypi上搜出来的重名库太多,很难搞清楚哪个才是你要的。

Python2和Python3的差异

简述

Python2中有很多固有的设计问题,例如:

  1. print是内置关键词。一般来说,关键词越少越好,因为关键词越少,语言的内核越简洁。
  2. 混同了bytes和unicode。
  3. /是整数除法。

这些问题在Python3中逐步给予了修复。

常规来说,修复问题最重要的是“向下兼容,逐步进行”。

然而上述问题几乎全部都是语言本质问题,不对语言进行伤筋动骨的大改是没有办法修复的。

因此Python3的预订是“不和Python2兼容”,这造成了Python社区目前2/3分裂的现状。

Python2和Python3有很多细节差异。但是大致来说,最主要就是上面提到的三项。

  1. Python2的print由关键字改为了函数。因此print ‘xxx’的写法就不合法了。
  2. 在Python3中,所有的字符串默认都是unicode。要处理原生数据需要用bytes。因此很多在Python2中能够很方便就处理过去的地方需要仔细思考是unicode还是bytes。
  3. Python3中/不是整数除法。

在下面这个连接里,收录了Python2和Python3的其他一些细节差异。

https://wiki.python.org/moin/Python2orPython3

Python2到3迁移

Python3带有2to3脚本,可以完成很多项目的迁移。

但是对于某些情况,他仍然不能自动的完成所有工作。

2到3迁移的主要问题在于,目前有很多库,仍然没有完成Python3的迁移工作。这导致使用这些库编写的程序很难在Python3上找到更好的(或者更习惯的)替代产品。

而这件事情是2to3脚本无法自动完成的。

我该用哪个版本/哪个更好

由于Python2已经停止维护,因此新开项目时请一律使用Python3。

教材教授了很多Python2写法,主要是考虑到接手项目中可能有大量遗留代码。对于这些代码,读懂维护是必要技能。

对现存代码。如果预期会被长期维护的,建议将代码迁移到Python3中。如果代码生命期不长,短期维护Python2代码也可接受。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值