第一部分、Python基础

Python是一门解释型的弱类型语言

Python的语法比较简单,采用缩进方式,写出来的代码就像下面的样子:

# print absolute value of an integer:
a = 100
if a >= 0:
    print(a)
else:
    print(-a)
  • 以#开头的语句是注释
  • 按照约定俗成的惯例,应该始终坚持使用4个空格的缩进。
  • Python程序是大小写敏感的

1.1 基本数据类型和变量

Python 中的变量不需要用关键字声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建

1.1.1 标准数据类型

Python 中有六个标准的数据类型:

  • Number(数字)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Set(集合)
  • Dictionary(字典)

Python 的六个标准数据类型中:

  • 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
  • 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。

1.1.2 Number(数字)

Python 支持 int、float、bool、complex(复数)

内置的 type() 函数可以用来查询变量所指的对象类型。

>>> a, b, c, d = 20, 5.5, True, 4+3j
>>> print(type(a), type(b), type(c), type(d))
<class 'int'> <class 'float'> <class 'bool'> <class 'complex'>

1.1.3 String(字符串)

Python中的字符串用单引号 ‘ 或双引号 “ 括起来,同时使用反斜杠 \ 转义特殊字符。

字符串支持 索引 (下标访问),第一个字符的索引是 0。单字符没有专用的类型,就是长度为一的字符串:

>>> word = 'Python'
>>> word[0]  # character in position 0
'P'
>>> word[5]  # character in position 5
'n'

索引还支持负数,用负数索引时,从右边开始计数:

>>> word[-1]  # last character
'n'
>>> word[-2]  # second-last character
'o'
>>> word[-6]
'P'

内置函数 len() 返回字符串的长度:

>>> s = 'supercalifragilisticexpialidocious'
>>> len(s)
34

1.1.4 Boolean(布尔值)

布尔值和布尔代数的表示完全一致,一个布尔值只有True、False两种值

1.1.5 None(空值)

空值是Python里一个特殊的值,用None表示。None不能理解为0,因为0是有意义的,而None是一个特殊的空值。

1.2 字符串和编码

在最新的Python 3版本中,字符串是以Unicode编码的,也就是说,Python的字符串支持多语言,例如:

>>> print('包含中文的str')
包含中文的str

对于单个字符的编码,Python提供了ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符:

>>> ord('A')
65
>>> ord('中')
20013
>>> chr(66)
'B'
>>> chr(25991)
'文'

1.2.1 字符串的格式化

在Python中,采用的格式化方式和C语言是一致的,用%实现,举例如下:

>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'

1.3 list和tuple

1.3.1 列表

用len()函数可以获得list元素的个数:

>>> len(classmates)
3

用索引来访问list中每一个位置的元素,记得索引是从0开始的。还可以用-1做索引,直接获取最后一个元素。

当索引超出了范围时,Python会报一个IndexError错误,所以,要确保索引不要越界,记得最后一个元素的索引是len(classmates) - 1。

>>> classmates = ['Michael', 'Bob', 'Tracy']
>> classmates[0]
'Michael'
>>> classmates[1]
'Bob'
>>> classmates[2]
'Tracy'
>>> classmates[-1]
'Tracy'
>>> classmates[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>

1.3.1.1 删除列表元素

可以使用 del 语句来删除列表的的元素,如下实例:

numbers=[1,2,3,4,5,6,7,8,9,10]
print(min(numbers))
print(max(numbers))
del numbers[2]
print(numbers)

1.3.1.2 列表的拷贝

以下都是深拷贝

number1=[1,[2,3],4]
number2=number1[:]
print(number2)

number2[1]=[222,333]
print('After change number1',number1)
print('After change number2',number2)

1.3.2 元组

tuple和list非常类似,但是tuple一旦初始化就不能修改,比如同样是列出同学的名字:

>>> classmates = ('Michael', 'Bob', 'Tracy')

现在,classmates这个tuple不能变了,它也没有append(),insert()这样的方法。其他获取元素的方法和list是一样的,你可以正常地使用classmates[0],classmates[-1],但不能赋值成另外的元素。

不可变的tuple有什么意义?因为tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple。

tuple的陷阱:当你定义一个tuple时,在定义的时候,tuple的元素就必须被确定下来

当元组只有一个成员的时候,一定要在后面加个逗号来区分元组和()运算符

schoolmates1 = (1,) # 元组 (1,)
print(schoolmates1)
schoolmates2 = (1) # 数字1
print(schoolmates2)

1.4 dict和set

1.4.1 dict

dict,就是JS中的对象。

>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95

如果访问字典中不存在的key,则会报错,所以更稳妥的方式是使用dict.get()方法,如果key不存在,默认返回None,或者自己指定默认值

print(d.get('sex'))
print(d.get('sex','male'))

二是通过in判断key是否存在:

>>> 'Thomas' in d
False

1.4.2 set

和js中的一致

第二部分、函数

2.1 定义函数

在Python中,定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。

def my_abs(x):
    if x >=0:
        return x
    else:
        return -x

2.1.1 空函数

如果想定义一个什么事也不做的空函数,可以用pass语句:

def nop():
    pass

pass语句什么都不做,那有什么用?实际上pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。

pass还可以用在其他语句里,比如:

if age >= 18:
    pass

缺少了pass,代码运行就会有语法错误。

2.1.2 参数检查

调用函数时,如果参数个数不对,Python解释器会自动检查出来,并抛出TypeError:

>>> my_abs(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: my_abs() takes 1 positional argument but 2 were given

2.1.3 返回多个值

import math

def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny

我们就可以同时获得返回值:

>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
151.96152422706632 70.0

原来返回值是一个tuple!但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值,所以,Python的函数返回多值其实就是返回一个tuple,但写起来更方便。

2.2 函数的参数

2.2.1 可变参数

即除了函数声明了的参数之外的所有参数tuple。即ES6中的rest参数(不是arguments哦)

def my_abs(x,y=20,*args,**kw):
    print(x,y,args,kw) # args (234,123,23)

my_abs(10,11,234,123,23,a=12,b=13)

2.2.2 关键字参数

关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。请看示例:

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

person('Adam', 45, gender='M', job='Engineer')
>>> name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

2.2.3 命名关键字参数

对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。如果要限制关键字参数的名字,就可以用命名关键字参数

def person(name, age, *, city, job):
    print(name, age, city, job)

和关键字参数*kw不同,命名关键字参数需要一个特殊分隔符,*后面的参数被视为命名关键字参数

调用方式如下:

>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

def person(name, age, *args, city, job):
    print(name, age, args, city, job)

第三部分、高级特性

3.1 切片

对列表和字符串进行截取

>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']
>>> L[1:3]
['Sarah', 'Tracy']

3.2 列表生成式

列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式。

举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]可以用list(range(1, 11)):

>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但如果要生成[1x1, 2x2, 3x3, …, 10x10]怎么做?方法一是循环:

>>> L = []
>>> for x in range(1, 11):
...    L.append(x * x)
...
>>> L
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

但是循环太繁琐,而列表生成式则可以用一行语句代替循环生成上面的list:

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

3.2 生成器

>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

我们讲过,generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

>>> g = (x * x for x in range(10))
>>> for n in g:
        print(n)

第四部分、函数式编程

4.1 匿名函数

关键字lambda表示匿名函数,冒号前面的x表示函数参数。

>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]

4.1 偏函数

偏函数可以简化参数操作。

当函数的某个参数是我们可以提前获知的,那我们就可以将它固定住!

# 定义一个取余函数,默认和2取余;
def mod(x,y=2):
  # 返回 True 或 False
  return x % y == 0

# 假设我们要计算和3取余,如果不使用partial()函数,那么我们每次调用mod()函数时,都要写y=3
mod(4,y=3)

# 使用partial()函数
from functools import partial
mod_3 = partial(mod,y=3)
mod_3(4)
mod_3(6)

第五部分、面向对象编程

5.1 类和实例

class Student(object):
    def __init__(self, name, score):
        self.name = name
        self.score = score

__init__是构造方法:

  • __init__方法的第一个参数永远是self,表示创建的实例本身

5.2 访问限制

class Student(object):
    def __init__(self, name, age):
        self.__name = name
        self.age = age
    
bart=Student("zjc",18)
print(bart.__name) # 报错
# print(bart._Student__name)
print(bart.age)

Python中可以在属性的名称前加上两个下划线__声明一个私有变量

双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。 不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量。但是非常不会建议你这么做。

5.3 继承

class Animal(object):
    def run(self):
        print('Animal is running...')

class Dog(Animal):
    pass

5.4 类的专有方法

- __init__ : 构造函数,在生成对象时调用
- __del__ : 析构函数,释放对象时使用
- __repr__ : 打印,转换
- __setitem__ : 按照索引赋值
- __getitem__: 按照索引获取值
- __len__: 获得长度
- __cmp__: 比较运算
- __call__: 函数调用
- __add__: 加运算
- __sub__: 减运算
- __mul__: 乘运算
- __truediv__: 除运算
- __mod__: 求余运算
- __pow__: 乘方

5.4.1 运算符重载

Python同样支持运算符重载,我们可以对类的专有方法进行重载,实例如下:

class Vector:
   def __init__(self, a, b):
      self.a = a
      self.b = b
 
   def __str__(self):
      return 'Vector (%d, %d)' % (self.a, self.b)
   
   def __add__(self,other):
      return Vector(self.a + other.a, self.b + other.b)
 
v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2) # Vector(7,8)

5.5 使用__slots__

正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。先定义class:

class Student(object):
    pass

然后,尝试给实例绑定一个属性:

>>> s = Student()
>>> s.name = 'Michael' # 动态给实例绑定一个属性
>>> print(s.name)
Michael

这样随便绑定什么属性都可以,若要是我们想限制以下属性怎么办呢?

为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

然后,我们试试:

>>> s = Student() # 创建新的实例
>>> s.name = 'Michael' # 绑定属性'name'
>>> s.age = 25 # 绑定属性'age'
>>> s.score = 99 # 绑定属性'score'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'score'

使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:

>>> class GraduateStudent(Student):
...     pass
...
>>> g = GraduateStudent()
>>> g.score = 9999

除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__。

5.6 使用@property

在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致可以把成绩随便改

s = Student()
s.score = 9999

这显然不合逻辑。为了限制score的范围,可以通过一个新增一个get,set方法来检查参数。

但是,上面的调用方法又略显复杂,没有直接用属性这么直接简单。

有没有既能检查参数,又可以用类似属性这样简单的方式来访问类的变量呢?

Python内置的@property装饰器就是负责把一个方法变成属性调用的:

class Student(object):

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

@property把一个getter方法变成属性,只需要加上@property就可以了, 此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值

参考链接: