Jimmy那些事儿

Python_基本概念与操作

数据类型

  • Number(数字)
    • int(整型)
    • float(浮点型)
    • complex(复数)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Set(集合)
  • Dictionary(字典)
  • Number(数字)


字符串

  • 字符串的输入

    • 单引号/双引号; 单引号
    • 三个引号
      1
      2
      3
      4
      5
      6
      7
      - **字符转义**:
      - `\ (反斜杠)`
      - `r`` ` 引号内部的字符串默认都不转义
      ```python
      r`ABC\.CSV$`
  • 合并 :运用加号 ( + ) 来进行合并字符串

  • 空白

    • 添加空白:制表符 (\t) 、换行符(\n)
    • 删除空白:两侧 - strip();左侧 - lstrip();右侧 - rstrip()
  • 赋值与变更 : 必须将变更后的对象赋值,因为字符串是不可更改的; f = f.strip()

  • 字符串 *(星号) 数字;表示字符串重复输出n次 print("." * 10)

  • 转换 : str()

  • 长度:len(string)

  • 格式化

    • 用%来实现字符串的格式化
1
2
3
4
binary = "binary"
do_not = "don't"
y = "Those who know %s and those whe %s." % (binary, do_not)
print(y)
字符串 含义
%d - digit 整数
%f - float 浮点数
%s - string 字符串
%x 十六进制整数
%% 一个%

如果你不太确定应该用什么,%s永远起作用,它会把任何数据类型转换为字符串


布尔值

  • and / or / not

空值

  • None,一个特殊的空值

可变与不可变对象

  1. str是不变对象,而list是可变对象
    • 对于可变对象,比如list,对list进行操作,list内部的内容是会变化的
    • 对于不可变对象,比如str,对str进行操作,str内部是不变的。
1
2
3
4
5
6
7
8
9
10
11
12
# 可变对象
>>> a = ['c', 'b', 'a']
>>> a.sort()
>>> a
['a', 'b', 'c']
# 不可变对象
>>> a = 'abc'
>>> a.replace('a', 'A')
'Abc'
>>> a
'abc' # 虽然字符串有个replace()方法,也确实变出了'Abc',但变量a最后仍是'abc'
  1. 始终牢记的是,a是变量,而’abc’才是字符串对象!有些时候,我们经常说,对象a的内容是’abc’,但其实是指,a本身是一个变量,它指向的对象的内容才是’abc’ 。
    • 当我们调用a.replace(‘a’, ‘A’)时,实际上调用方法replace是作用在字符串对象’abc’上的,而这个方法虽然名字叫replace,但却没有改变字符串’abc’的内容。相反,replace方法创建了一个新字符串’Abc’并返回,如果我们用变量b指向该新字符串,就容易理解了,变量a仍指向原有的字符串’abc’,但变量b却指向新字符串’Abc’了
    • 对于不变对象来说,调用对象自身的任意方法,也不会改变该对象自身的内容。相反,这些方法会创建新的对象并返回,这样,就保证了不可变对象本身永远是不可变的。

a-b-to-2-strs


数据类型转换

函数 描述
int(x [,base]) 将x转换为一个整数
long(x [,base] ) 将x转换为一个长整数
float(x) 将x转换到一个浮点数
complex(real [,imag]) 创建一个复数
str(x) 将对象 x 转换为字符串
repr(x) 将对象 x 转换为表达式字符串
eval(str) 用来计算在字符串中的有效Python表达式,并返回一个对象
tuple(s) 将序列 s 转换为一个元组
list(s) 将序列 s 转换为一个列表
set(s) 转换为可变集合
dict(d) 创建一个字典。d 必须是一个序列 (key,value)元组。
frozenset(s) 转换为不可变集合
chr(x) 将一个整数转换为一个字符
unichr(x) 将一个整数转换为Unicode字符
ord(x) 将一个字符转换为它的整数值
hex(x) 将一个整数转换为一个十六进制字符串
oct(x) 将一个整数转换为一个八进制字符串


数据结构

元组 - tuple

  • what :元组是一种不可变序列
    • 元组和列表一样可以使用索引、切片来取值。
    • 创建元组后不能在原地进行修改替换等操作
    • 元组支持嵌套,可以包含列表、字典和不同元组
  • why :元组的不可变性,在保证一个程序安全方面起到很大作用。

创建tuple

  • 元组是用小括号( )包括起来的,( )括号中的元素用逗号分割,这样就完成元组的创建了
  • tuple函数进行创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>>(1,2,3)
(1,2,3)
# 如果新建时没有用( )括号包括,只用逗号进行分隔python也会把它认做为元组
>>>1,2,3
(1,2,3)
# 如果你新建的元组中只有一个值,那么请记得在这个值后边加上逗号
>>>1,
(1,)
# 如果没有逗号,python只当把它当做整型来处理。
>>>1
----------------------------------------------------------------------------------------------------------------------------------------
# 用到tuple函数的功能:它可以一个列表为参数,把它转换为元组。
>>>tuple([7,8,9])
(7,8,9)
>>>tuple('hello') # 把字符串'hello'中的每一个字母,都做为一个新的元素添加到新元组中
('h', 'e', 'l', 'l', 'o')

修改元组

元组是不可变的,类似字符串,不能在原处修改内容,但我们可以把它的类型做下转换,例如把一个元组转换为一个列表进行修改,之后再转换成元组

索引 - 切片


列表list - 基本功能

  • what :可以包含不同类型的数据对像,同时它是一个有序的集合;同时,它支持添加 append、插入 insert、修改、删除del等操作

1. 创建

  1. list列表理解为任意对像的序列,只要把需要的参数值放入到中括号[ ]里面就可以了
  2. 用函数list()创建
1
2
3
4
5
6
names = ['ada','amy','ella','sandy']
list()
# 列表可以包含不同类型对像,也支持嵌套:
>>>a = ['a',567,['adc',4,],(1,2)]

2. 查找(索引) - 切片

方括号中索引值,半闭半开区间;左侧包括,右侧不包括

  1. 使用方括号[ ] 冒号 :
  2. 索引从0开始;
  3. 最后一个列表的元素为-1 (倒数第二个为-2,从末尾开始数正常)
1
names[1:2]

3. 修改

  1. 列表是有序的,可以通过list下标来修改特定位置的值
1
2
3
4
>>>a = [1,9,9]
>>>a [0] = 9
>>>a
[9,9,9]

4. .append() / .insert() - 添加

  1. 末尾添加 - append(obj) / append(“string”)
  2. 插入元素 - insert(n, obj) /insert(n, “string”) motorcycles.insert(0, 'ducati')
    • 第一个实参n表示插入的索引位置;这种操作会使得表中所有位于该索引之后的元素往右移一个位置


5. del & pop & remove - 删除

  1. 根据位置直接删除 - del del motocycles[0]

  2. 根据位置删除 - pop()

    • 默认情况下,弹出列表末尾的一个元素 motorcycles.pop()
    • 指定索引位置,则弹出该位置的元素 motorcycle.pop(1)
    • 每次只能删除一个索引位置的元素
  1. 根据值删除 - remove()

    • remove 只删除第一个指定的值;若要删除的值在列表中出现多次,需要用循环来操作
  • motorcycles.remove('string') / motorcycles.remove(obj)

如果被弹出的元素不再需要使用,就选择del


6. .sort() / sorted() - 排序

  1. 永久性排序 - 方法sort() cars.sort(reverse=True) 默认按字母顺序进行升序排列
  2. 临时性排序 - 函数sorted() sorted(cars, reverse = True)

7. .reverse - 反转

  1. 方法reverse() cars.reverse()

反转是在原有的基础上进行颠倒;不同于按倒序排列

8. 长度 - len()

Python计算列表list元素时从1开始。但索引是从0开始的;所以,在确定列表长度时会遇到差一


列表 - 操作

1. 遍历列表 - 循环

  1. for循环对列表的每个元素进行访问。
1
2
for magician in magicians:
print(magician.title() + ", that was a great trick!")

使用单数、复数的名称,有利于判断是单个列表元素还是整个列表

冒号的使用

缩进的代码必须是循环的一部分;没有缩进的代码只执行一次

2. 创建数值列表- range

  1. 函数range() ,半闭半开区间;
1
range(1,100,2) # c为步长

3. 列表推导式

  1. 将for循环和创建新元素的代码合并成一行;(语法糖)
1
2
3
4
5
6
7
8
9
10
11
12
13
resutl = [ expr_value for value in collection if condition]
# expr_value 为value的表达式 squares = [value**2 for value in range(1,11)]
# 若缺少变量result,则直接返回符合的值
# ----等价于---------
result = []
for value in collction:
if condition:
result.append(value)
# ---------示例--------------
strings = ['a', 'as', 'bat', 'car', 'dove', 'python']
[x.upper() for x in strings if len(x) > 2]

字典推导式

1
2
> dict_result = { key-expr: value-expr for value in collection if condition} # 区别在与用 花括号
>

>

集合推导式

1
2
> set_result = {expr for value in collection if condition}
>


4. 使用列表的一部分 - 切片/复制

  1. 切片 :指定第一个元素、最后一个元素的索引;
1
2
3
4
5
players[0:3] # 列表中实际的 1~3个元素;
players[:4] # 未指定第一个元素的索引,则从0开始
players[2:] # 未指定最后一个元素的索引,则到末尾; 等价于players[2:-1]
players[-3:] # 从倒数第3个元素的开始到末尾
players[:] # 列表的所有元素
  1. 复制列表 :复制,两者之间没有联系。通过复制来实现
1
friend_food = my_food[:]
  1. 若直接赋值,则表示指向同一个对象;两者之间有关联;
1
2
3
4
5
6
7
8
9
10
11
12
13
friend_food = my_food
# --------------示例----------------
my_foods = ['pizza', 'falafel', 'carrot cake']
friend_foods = my_foods
my_foods.append('cannoil')
print(my_foods)
print(friend_foods)
>>>
['pizza', 'falafel', 'carrot cake', 'cannoil']
['pizza', 'falafel', 'carrot cake', 'cannoil']


字典 - dict

  • what :字典是一系列键-值对。每个键都与一个值相关联。

一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值冲掉

dict是用空间来换取时间的一种方法,并且需要牢记的第一条就是dict的key必须是不可变对象

字符串、整数等都是不可变的,因此,可以放心地作为key

dict全称dictionary,在其他语言中也称为map

1. 创建与访问

  1. 创建:
    • 用花括号、冒号来实现
    • dict()
1
2
3
4
p = {}
p = {'color': 'green',
'points': 5, # 最后一个仍留有逗号,为以后添加键值做准备
}
  1. 访问:指定方括号内的键
1
p['color']

2. 添加与删除

  1. 添加键-值对:直接赋值,若键不存在则创建
1
2
p['x_position'] = 0
p['y_position'] = 25
  1. 删除
1
2
3
del p['color']
p.pop('color') # 指定的是键,而非值; 在列表list中,pop指定的是索引

3. 遍历字典

  1. 遍历所有的键-值对 - .items()
  2. 遍历所有的键 - .keys()
  3. 遍历所有的值 - .values()
1
2
3
4
5
6
for key, value in user_0.items():
pass
for key in user_0.keys():
pass
for value in user_0.values():
pass
  1. 顺序遍历所有的键 - for key in sorted(user_0.keys()):


集合 - set

  • what :没有重复的键。
  • why :重复元素在set中自动被过滤

set和dict的唯一区别仅在于没有存储对应的value,但是,set的原理和dict一样,所以,同样不可以放入可变对象

1. 创建

创建一个set,需要提供一个list作为输入集合

1
2
3
>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}

传入的参数[1, 2, 3]是一个list,而显示的{1, 2, 3}只是告诉你这个set内部有1,2,3这3个元素,显示的顺序也不表示set是有序的>>> s = set([1, 2, 3])>>> s{1, 2, 3}

2.添加与删除

  1. 添加 - .add()
  2. 删除 - .remove()


条件判断

1.条件测试

  1. 相等 ==
  2. 不相等 !=
  3. 检查多个条件:and / or
    • 为改善可读性,建议加上括号 (age > 21) and (age_1 > 21)

2. 检查特定值是否包含在[列表]

  1. in / not in

3. if语句

  1. if语句
  2. if - else 语句
  3. if - elif - else 语句

它是从上往下判断,如果在某个判断上是True,把该判断对应的语句执行后,就忽略掉剩下的 elif 和 else

只要x是非零数值、非空字符串、非空list等,就判断为True,否则为False

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# if语句
if x:
print(x)
# if-else 语句
if x:
pass
else:
pass
# if-elif-else语句
if x:
pass
elif y:
pass
else:
pass


4. input - 有问题的条件判断

input()返回的数据类型是str,str不能直接和整数比较,必须先把str转换成整数

1
2
3
4
5
birth = input('birth: ')
if birth < 2000:
print('00前')
else:
print('00后')


循环

break & continue

break语句可以在循环过程中直接退出循环,而continue语句可以提前结束本轮循环,并直接开始下一轮循环

  1. break
    • 立即退出循环; 即该for/while循环停止,不在运行任何代码
    • 嵌套循环中,仅跳出当前所在for/while循环
  2. continue
    • 忽略余下代码,直接返回for/while循环的开头。即立刻开始第二次循环
1
2
3
4
5
6
7
8
9
10
## 返回100以内的质数
# k = ''
for i in range(50:101): # 第1层循环
for a in range(2:i): # 第2层循环 (从2开始作为除数,并且终止于i-1)
if i % a == 0:
break # 此处break跳出的是for a 这个第2层循环;即该for a循环不在执行,i 从 50增加到51
if a == i - 1:
print(i)
# k = k +str(i) + '\n'
# print(k)


函数

1. 定义函数

定义函数时,需要确定函数名和参数个数;

如果有必要,可以先对参数的数据类型做检查;

  1. 定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回
  2. 函数体内部的语句在执行时,一旦执行到return时,函数就执行完毕,并将结果返回。
  3. 如果没有return语句,函数执行完毕后也会返回结果,只是结果为None
1
2
3
4
5
def my_abs(x):
if x > 0:
return x
else x < 0:
return -x

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

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

1
2
3
> def nop():
> pass
>
  1. 参数检查
    • 调用函数时,如果参数个数不对,Python解释器会自动检查出来,并抛出TypeError:
    • 当传入了不恰当的参数时,内置函数abs会检查出参数错误,而我们定义的my_abs没有参数检查,会导致if语句出错,出错信息和abs不一样。
1
2
3
4
5
6
7
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError(('bad operand type'))
if x >= 0:
return x
else:
return -x
  1. 返回多个值

    1
    2
    3
    4
    5
    import math
    def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny

2. 参数

1. 形参与实参

  • 形参:函数完成工作时所需的一项信息
  • 实参:调用函数时传递给函数的信息
  • 位置实参:顺序很重要
  • 关键字实参:传递给函数的 名称 - 值对

2. 参数的默认值

  • 在形参部分赋值
  • 必选参数在前,默认参数在后
1
2
3
4
5
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L

3. 可选参数(实参)

  1. 使用默认值让实参变为可选的:空字符串 + 置于末尾
1
2
def get_formatted_name(first, last, middle=''):
print(first + middle + last)

4.1 任意数量的(可变)实参

允许传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple 【关键区别】。

  1. 在形参之前加上一个星号(*) : 创建一个名为numbers的空元组tuple,并将所有收到的值封装到这个元组中
1
2
3
4
5
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
  1. 如果已经有一个list或者tuple,要调用一个可变参数;允许你在list或tuple前面加一个星号(*),把list或tuple的元素变成可变参数传进去
1
2
3
4
5
6
>>> nums = [1, 2, 3]
>>> calc(*nums)
14
# ---------其他方式-------------
calc(nums[0], nums[1], nums[2])
  1. 位置实参 + 任意数量实参将位置实参写在前 + 任意数量实参仍用 *形参名
1
2
def make_pizza(size, *toppings):
print(size + toppings)

4.2 任意数量的关键字实参

允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict 【关键区别】

  1. 形参之前加上两个星号(\) :创建一个名为person的空字典,并将收到的所有名称-值对封装到这个字典中**
    • 该方式不限制关键字参数的名字
1
2
3
4
5
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
person('bob' , 35, city = 'Beijing', tall =32)
>>>name: bob age: 35 other: {'city': 'Beijing', 'tall': 32}
  1. 限制关键字参数的名字,就可以用命名关键字参数

    • 命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数 【建议采用此种命名方法】
    1
    2
    3
    4
    5
    def person(name, age, *, city, job):
    print(name, age, city, job)
    person('Kate', 21, args=32, city='shanghai', job='doctor')
    [out]: Kate 21 32 shanghai doctor
    • 如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
      • 但该可变参数为元组,会用圆括号括起来
    1
    2
    3
    4
    5
    def person(name, age, *args, city, job):
    print(name, age, args, city, job)
    person('Kate', 21, 32, city='shanghai', job='doctor')
    [out]: Kate 21 (32,) shanghai doctor
  1. 命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错
  2. 使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个*作为特殊分隔符。如果缺少*,Python解释器将无法识别位置参数和命名关键字参数

5. 参数组合

  1. 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数
    • 可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def f1(a, b, c=0, *args, **kw):
print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
# *args 作为一个任意数量的实参,为一个元组
f1(1, 2, 3, 'a', 'b', x=99)
[out]: a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
--------------------------------------------------------------------
def f2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)
# *,d 作为一个整体,为命名关键字参数
# **kw 为关键字参数(实参名字可任意命名)
f2(1, 2, d=99, ext=None)
[out]: a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}
  1. 通过一个tuple和dict,你也可以调用上述函数:
1
2
3
4
5
6
7
args = (1, 2, 3, 4)
kw = {'d': 99, 'x': '#'}
f1(*args, **kw)
[out]: a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw = {'x': '#'}
  • 其他

函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名” a =abs ; a(-1)

6. 参数小结

  1. 默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误!
  2. 要注意定义可变参数和关键字参数的语法:
  3. *args是可变参数,args接收的是一个tuple;
  4. **kw是关键字参数,kw接收的是一个dict。
  5. 调用函数时如何传入可变参数和关键字参数的语法:
    1. 可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过*args传入:func(*(1, 2, 3))
    2. 关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过**kw传入:func(**{'a': 1, 'b': 2})
  6. 使用*args**kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。
  7. 命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。
  8. 定义命名的关键字参数在没有可变参数的情况下不要忘了写分隔符*,否则定义的将是位置参数


3. 传递实参 - 列表

1
2
3
4
5
6
7
8
9
10
11
12
13
def greet_users(names):
'''doc strings'''
for name in names:
msg = "Hello, " + name.title() + "!"
print(msg)
usernames = ['hanah', 'ty', 'magto']
greet_users(usernames)
[out]:
Hello, Hannah!
Hello, Ty!
Hello, Magot!

4. 返回值

函数体内部可以用return随时返回函数结果;

函数执行完毕也没有return语句时,自动return None。

函数可以同时返回多个值,但其实就是一个tuple。

  1. 使用return语句将值返回到函数的调用行
1
2
3
4
5
6
7
8
def get_formatted_name(firsr, last):
full_name = first + ' ' + last
return full_name.title()
#--------------------------------------------------------
def get_formatted_name(firsr, last):
full_name = {'first': first, 'last': last} # 返回字典
return full_name

5. 递归函数

每个函数应只负责一项具体的工作。若执行的任务太多,可在函数中调用另一个函数

在函数内部,可以调用其他函数。所谓递归函数,一个函数在内部调用自身本身

举个例子,我们来计算阶乘n! = 1 x 2 x 3 x ... x n,用函数fact(n)表示,可以看出:

fact(n) = n! = 1 x 2 x 3 x … x (n-1) x n = (n-1)! x n = fact(n-1) x n

所以,fact(n)可以表示为n x fact(n-1),只有n=1时需要特殊处理

1
2
3
4
def fact(n):
if n==1:
return 1
return n * fact(n - 1)

使用递归函数的优点是逻辑简单清晰,理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。缺点是过深的调用会导致栈溢出。

针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。

5. 保存与调用

如果你已经把my_abs()的函数定义保存为abstest.py文件了,那么,可以在该文件的当前目录下启动Python解释器,用from abstest import my_abs来导入my_abs()函数,注意abstest是文件名(不含.py扩展名)

1
from abstest import my_abs # 导入my_abs() 函数


函数式编程

函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。

  • what :函数式编程(请注意多了一个“式”字)——Functional Programming,虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。
    • 计算机(Computer)和计算(Compute)
      • 计算机的层次上,CPU执行的是加减乘除的指令代码,以及各种条件判断和跳转指令,所以,汇编语言是最贴近计算机的语言。
      • 计算则指数学意义上的计算,越是抽象的计算,离计算机硬件越远。
    • 对应到编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如Lisp语言。
    • 函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量
    • 任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。
      • 允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
  • why :函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

高阶函数

  1. 变量可以指向函数

    • 函数本身赋值给变量
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    >>> abs(-10)
    10
    >>> abs
    <built-in function abs>
    # abs(-10)是函数调用,而abs是函数本身。
    -----------------------------------------------------------------------------
    >>> f = abs
    >>> f
    <built-in function abs>
    # 说明变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同。
  2. 函数名也是变量

    函数名其实就是指向函数的变量 。对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!

  3. 传入函数

    1
    2
    3
    4
    5
    6
    7
    8
    def add(x, y, f): # f = abs
    return f(x) + f(y)
    x = -5
    y = 6
    f = abs
    f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
    return 11


切片&迭代

1. 切片

tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple

字符串’xxx’也可以看成是一种list,每个元素就是一个字符。

  1. 切片 :指定第一个元素、最后一个元素的索引;
1
2
3
4
5
players[0:3] # 列表中实际的 1~3个元素;
players[:4] # 未指定第一个元素的索引,则从0开始
players[2:] # 未指定最后一个元素的索引,则到末尾; 等价于players[2:-1]
players[-3:] # 从倒数第3个元素的开始到末尾
players[:] # 列表的所有元素
  1. 切片的第三个值为步长step
1
2
3
4
5
6
7
8
# 前10个数,每两个取一个:
>>> L[:10:2]
[0, 2, 4, 6, 8]
# 所有数,每5个取一个:
>>> L[::5]
[0, 5, 10, 15, 20, 25]
  1. 字符串的选取
1
2
3
# 字符串'xxx'也可以看成是一种list,每个元素就是一个字符。
>>> 'ABCDEFG'[:3]
'ABC'
  1. 复制列表 :复制,两者之间没有联系。通过复制来实现
1
friend_food = my_food[:]

2. 迭代

  • what :如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。
    • 这些可以直接作用于for循环的对象统称为可迭代对象:Iterable
      • 集合数据类型:列表list、元组tuple、字典dict、字符串string、集合set
      • generator,包括生成器和带yield的generator function
    • 可以使用isinstance()判断一个对象是否是Iterable对象

迭代操作

  1. list或tuple迭代
1
2
for i in list:
print(i)
  1. 字典dict迭代

dict迭代的是key。

如果要迭代value,可以用for value in d.values(),如果要同时迭代key和value,可以用for key, value in d.items()。

1
2
3
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d: # value in d.values() / key, value in d.items()
print(key)
  1. 字符串迭代
1
2
>>> for ch in 'ABC':
print(ch)
  1. 索引和元素同时迭代:Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:
1
2
3
4
5
6
>>> for i, value in enumerate(['A', 'B', 'C']):
print(i, value)
0 A
1 B
2 C

判断迭代

  • 判断是否可迭代:通过collections模块的Iterable类型判断
1
2
3
4
5
6
7
8
9
10
11
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False


3. 列表生成式

  1. 将for循环和创建新元素的代码合并成一行;(语法糖)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
resutl = [ expr_value for value in collection if condition]
# expr_value 为value的表达式 squares = [value**2 for value in range(1,11)]
# 若缺少变量result,则直接返回符合的值
# ------------------------等价于---------------------------------
result = []
for value in collction:
if condition:
result.append(value)
# ---------------------示例-------------------------
strings = ['a', 'as', 'bat', 'car', 'dove', 'python']
[x.upper() for x in strings if len(x) > 2]
[x * x for x in range(1, 11) if x % 2 == 0]

字典推导式

1
2
> dict_result = { key-expr: value-expr for value in collection if condition} # 区别在与用 花括号
>

>

集合推导式

1
2
> set_result = {expr for value in collection if condition}
>
  1. 两层循环
1
2
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']


4. 生成器

  • what :一边循环一边计算的机制,称为生成器:generator。

如果列表元素可以按照某种算法推算出来,这样就不必创建完整的list,从而节省大量的空间。

  1. 创建生成器generator
    • 只要把一个列表生成式的 [] 改成 (),就创建了一个generator:
1
2
3
4
5
L = [x * x for x in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
g = (x * x for x in range(10))
<generator object <genexpr> at 0x1022ef630>

创建Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator

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

  1. 返回值
    • next()函数获得generator的下一个返回值
    • 通过迭代
1
2
3
4
5
6
7
8
9
>>> next(g)
0
>>> next(g)
1
# ---------------------通过迭代----------------------------
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

  • 生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator
    • listdictstrIterable变成Iterator可以使用iter()函数:
1
2
3
4
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

listdictstr等数据类型不是Iterator

因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。


面向过程 vs. 对象的程序设计

  • what :面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数

  • 面向过程 vs. 对象编程:

    • 面向过程的程序设计:把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
    • 面向对象的程序设计:把计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。

    在Python中,所有数据类型都可以视为对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

  • 面向过程

1
2
3
4
5
6
7
8
## 假设我们要处理学生的成绩表,为了表示一个学生的成绩
# 面向过程的程序可以用一个dict表示
std1 = { 'name': 'Michael', 'score': 98 }
std2 = { 'name': 'Bob', 'score': 81 }
# 处理学生成绩可以通过函数实现,比如打印学生的成绩
def print_score(std):
print('%s: %s' % (std['name'], std['score']))
  • 面向对象
    • 采用面向对象的程序设计思想,我们首选思考的不是程序的执行流程,而是Student这种数据类型应该被视为一个对象,这个对象拥有name和score这两个属性(Property)
1
2
3
4
5
6
7
8
9
# 如果要打印一个学生的成绩,首先必须创建出这个学生对应的对象,然后,给对象发一个print_score消息,让对象自己把自己的数据打印出来。
class Student(object):
def __init__(self, name, score):
self.name = name
self.score = score
def print_score(self):
print('%s: %s' % (self.name, self.score))


创建 - 类

类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;

方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据;

通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同:

class后面紧接着是类名,即Dog,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

attribute为该类的属性;def 定义的该类的行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Dog():
"""一次模拟小狗的简单尝试"""
# ----------直接定义属性----------------- # 调用类时无需再传入外部参数,此时已默认其属性值
name = 'Kite'
age = 3
#------------初始化属性------------------ # 调用类时必须传入外部参数
def __init__(self, name, age):
"""初始化属性 name 和 age"""
self.name = name
self.age = age
self.color = 'white' # 直接定义属性,且该属性无需出现在()中;调用类时也无需再传入外部参数,此时已默认其属性值
def sit(self):
"""模拟小狗被命令时下蹲"""
print(self.name.title() + " is now sitting.")
def roll_over(self,n):
"""模拟小狗被命令时打滚n次"""
self.n = n
print(self.name.title() + " rolled over " + self.n + "times!")
  • __init__ : 初始化属性,此类方式在调用类时需传入外部参数

    有了init方法,在创建实例的时候,就不能传入空的参数了,必须传入与init方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:

  • self :在类中定义属性,第一个参数永远是self,表示创建的实例本身。因此,在方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

    即,将外部参数的属性传入到内部


创建 - 实例

因为类时一个抽象的概念,在创建具体的实例之前,必须先有一个容器;

1
2
3
4
5
6
# 已创建了默认属性
my_dog = Dog()
my_dog
# 未创建默认属性,而是初始化属性
my_dog = Dog('willie', 6) # 必须传入外部参数

访问

  1. 采用方法的方式来进行访问
1
2
3
4
my_dog.name
[out]: 'willie'
my_dog.roll_over(2)
  1. 限制访问
  • 要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问
1
2
3
4
5
6
7
8
9
10
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
bart = Student('Bart Simpson', 98)

改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name实例变量.__score了:

1
2
3
4
>>> bart.__name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
  • 获取限制访问的变量增加方法 - 给Student类增加get_name和get_score这样的方法
1
2
3
4
5
6
7
8
9
10
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
bart.get_name
  • 允许外部代码修改 : 增加方法 - set_score方法
1
2
3
4
5
class Student(object):
...
def set_score(self, score):
self.__score = score

需要注意的是,在Python中,变量名类似xxx的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用namescore这样的变量名。

有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。


修改属性

  1. 直接修改属性的值
1
my_dog.color = "black"
  1. 通过方法修改属性
1
2
3
4
5
6
7
class Dog():
--snip--
def update_color(self, color):
self.color = color
my_dog.color('black')


继承与重写

一个类继承另一个类时,会自动获得另一个类的所有属性和方法。原有的类:父类;新的类:子类;

  • 创建子类时,父类必须包含在当前文件夹,且位于子类的前面
  • 必须在括号内指定内类的名称
  • 使用super().__init__将父类和子类关联起来,使其包含父类的所有属性
1
2
3
4
5
6
7
8
9
10
11
class Car():
--snip--
class ElectricCar(Car): # 括号中为父类的名称
def __init__(self, make, model, year):
super().__init__(make, model, year) # 通过super().__init__将父类和子类关联,并获得父类的所有属性
self.batter_size = 70 # 可同时新建属性,同时指定默认值
# --------------重写父类中的方法---------------------------
def fill_gas_tank(): # 当定义的方法中括号中的内容为空,表示忽略该方法
print("This car doesn't need a gas tank.")

导入类

1
2
3
4
5
6
7
8
9
10
# 导入指定的类
from filename import classname1, classname2
from car import Car, ElectricCar
# 导入整个模块
import filename
# 导入模块中的所有类
from filename import *



##主要函数


对象类型

  1. type() - 判断对象类型
  2. instance() - 判断一个对象是否为某种类型:返回布尔值Ture/Falses
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 指定判断类型
>>> isinstance('a', str)
True
>>> isinstance(123, int)
True
# 先创建判断的对象
>>> a = Animal()
>>> d = Dog()
>>> h = Husky()
>>> isinstance(h, Husky)
True
# 还可以判断一个变量是否是某些类型中的一种
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True

能用type()判断的基本类型也可以用isinstance()判断

  1. 获得对象的所有属性与类型 - dir()


字符串操作

替换 - replace

1
2
3
4
5
replace(old, new[, max]) # max,可选字符串, 替换不超过 max 次
relpace("a","b") # 将a替换为b
val.replace(",", "::")

分隔 - split - string转list

spilt - 通过指定的分隔符将字符串拆分为一组子串

  • 字符串会转为列表list
1
2
3
4
5
spilt(",") # 分隔后的字符串会转变为列表格式list
val = 'a,b, guido'
val.split(',')
# ['a', 'b', ' guido']

去空格 - strip

strip - 去除空格(含换行符),相当于对各个元素执行x.strip()

1
2
3
4
5
strip()
val.strip()
val.lstrip()
val.rstrip()

联接 - join - list转string

join - 联接其他字符串序列的分隔符

  • [列表转字符串]
1
2
3
4
','.join(val) # 将val转为string
"::".join(val) # 将::作为分隔符,联接对象val中的各个子串
## 转化之后,可将list转变为str


其他

1
2
3
4
count # 返回子串在字符串中出现的次数(非重叠)
startswith / endswith # 判断是否以某子串开始或结束,返回True或False
find / frind # 返回第一个/最后一个发现的第一个只字符所在的位置;若没有找到,则返回 -1 val.find(",")
index # 返回子串第一个字符所在的位置;若没有找到,引发 ValueError


函数 VS. 方法

若为方法,也直接在符合的对象之后进行引用。

  • DataFrame.xxx :数据框类型的对象
  • datetime.xxx:时间类型的对象


函数

函数function —— A series of statements which returns some value toa caller. It can also be passed zero or more arguments which may beused in the execution of the body.

  • 从定义的角度上看,函数(function)就相当于一个数学公式,它理论上不与其它东西关系,它只需要相关的参数就可以。所以普通的在module中定义的称谓函数是很有道理的。
  • 函数是一段代码,通过名字来进行调用。它能将一些数据(参数)传递进去进行处理,然后返回一些数据(返回值),也可以没有返回值。所有传递给函数的数据都是 显式传递


方法

方法method —— A function which is defined inside a class body. Ifcalled as an attribute of an instance of that class, the methodwill get the instance object as its first argument (which isusually called self).

  • 方法,它是与某个对象相互关联的,也就是说它的实现与某个对象有关联关系。这就是方法。虽然它的定义方式和函数是一样的。也就是说,在Class定义的函数就是方法。
    • 方法的定义可以是这样的,与某个对象进行绑定使用的函数。注意哦。绑定不是指” .”这个符号,这个符号说实在的只有域名的作用。绑定在这里是指,会默认赋值该绑定的对象。
  • 方法也是一段代码,也通过名字来进行调用,但它跟一个对象相关联。方法和函数大致上是相同的,但有两个主要的不同之处:
    1. 方法中的数据是隐式传递的;
    2. 方法可以操作类内部的数据(请记住,对象是类的实例化 。类定义了一个数据类型,而对象是该数据类型的一个实例化)


  • 从上面可以看出, 别的编程语言一样, Function也是包含一个函数头和一个函数体, 也同样支持0到n个形参,而Method则是在function的基础上, 多了一层类的关系, 正因为这一层类, 所以区分了 functionmethod.而这个过程是通过 PyMethod_New实现的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
PyObject *
PyMethod_New(PyObject *func, PyObject *self, PyObject *klass)
{
register PyMethodObject *im; // 定义方法结构体
im = free_list;
if (im != NULL) {
free_list = (PyMethodObject *)(im->im_self);
PyObject_INIT(im, &PyMethod_Type); // 初始化
numfree--;
}
else {
im = PyObject_GC_New(PyMethodObject, &PyMethod_Type);
if (im == NULL)
return NULL;
}
im->im_weakreflist = NULL;
Py_INCREF(func);
/* 往下开始通过 func 配置 method*/
im->im_func = func;
Py_XINCREF(self);
im->im_self = self;
Py_XINCREF(klass);
im->im_class = klass;
_PyObject_GC_TRACK(im);
return (PyObject *)im;
  • 以本质上, 函数和方法的区别是: 函数是属于 FunctionObject, 而 方法是属 PyMethodObject
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def aa(d, na=None, *kasd, **kassd):
pass
class A(object):
def f(self):
return 1
a = A()
print '#### 各自方法描述 ####'
print '## 函数 %s' % aa
print '## 类方法 %s' % A.f
print '## 实例方法 %s' % a.f
#输出结果:
#### 各自方法描述 ####
## 函数 <function aa at 0x000000000262AB38>
## 类方法 <unbound method A.f>
## 实例方法 <bound method A.f of <__main__.A object at 0x0000000002633198>>

【Bound Method 和 Unbound Method】

method 还能再分为 Bound MethodUnbound Method, 他们的差别是什么呢? 差别就是 Bound method 多了一个实例绑定的过程!
A.funbound method, 而 a.fbound method, 从而验证了上面的描述是正确的!

详细参见:https://segmentfault.com/a/1190000009157792


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
>>> def fun():
pass
>>> type(fun)
<class'function'> #没有问题
>>> class Cla():
def fun():
pass
@classmethod
def fun1(cls):
pass
@staticmethod
def fun2():
pass
>>> i=Cla()
>>>Cla.fun.__class__
<class'function'> #为什么还是函数
>>>i.fun.__class__ #这个还像话
<class 'method'>
>>>type(Cla.fun1)
<class 'method'>   #这里又是方法
>>> type(i.fun1)
<class 'method'>    #这里仍然是方法
>>>type(Cla.fun2)
<class 'function'>   #这里却是函数
>>> type(i.fun2)
<class 'function'>    #这里却是函数
  1. 普通方法(老版中直接就是”instancemethod”)在module中与在Class中定义的普通函数,从其本身而言是没有什么区别的,他们都是对象函数属性。 之所以会被说在Class中的定义的函数被称为方法,是因为它本来就是面向将来的实例对象的,其实他们就是实例方法,这些方法是与实例相联系的(从实例出发访问该函数会自动赋值)。所以你从Class访问仍然是一个函数
  2. 类方法(”classmethod”),因为类同样是对象,所以如果函数与类进行联系了话(与实例方法一样的模式)那么就能够这么说了!
  3. 静态方法,虽然定义在内部,并且也较方法,但是却不与任何对象联系,与从类访问方法是一样的,他们仍然是函数。



【实战演练】

for循环

1
2
3
4
5
6
7
for i in range(1,4):
a = str(i) + '.' + i*'caihf'
print (a)
1.caihf
2.caihfcaihf
3.caihfcaihfcaihf


打开与写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
with open ('whodata.txt') as f:
a = f.readlines()
['This is a test.\n', '\n', 'Beautiful is better than ugly.\n', 'Explicit is better than implicit.\n', 'Simple is LbOMONEetter than complex.\n']
# ---------------------------
c=''
for i in range(0,len(a)):
b = str(i+1) + '.' + a[i]
c= b+c
print(c)
1.This is a test.
2.
3.Beautiful is better than ugly.
4.Explicit is better than implicit.
5.Simple is LbOMONEetter than complex.
6.Complex is better than complicated.