Python 【知识点整理】【持续更新】

Jack · 2021年08月16日 · 120 次阅读
本帖已被设为精华帖!

列表

  1. 列表是一个可以放置任意数据类型的集合

    举例:

l = [1, 2, 'hello', 'world'] # 列表中同时含有int和string类型的元素
l
[1, 2, 'hello', 'world']
  1. 列表是动态的

举例:

l = [1, 2, 3, 4]
l[3] = 40 # 和很多语言类似,python中索引同样从0开始,l[3]表示访问列表的第四个元素
l
[1, 2, 3, 40]
l.append(5) # 添加元素5到原列表的末尾
l
[1, 2, 3, 40, 5]
  1. 支持负索引

举例:

l = [1, 2, 3, 4]
l[-1]
4
  1. 切片

举例:

l = [1, 2, 3, 4]
l[1:3] # 返回列表中索引从1到2的子列表
[2, 3] 
  1. 嵌套 (多维数组)

举例:

l = [[1, 2, 3], [4, 5]] # 列表的每一个元素也是一个列表
  1. 存储方式

​ 由于列表是动态的,所以它需要存储指针,来指向对应的元素。另外,由于列表可变,所以需要额外存储已经分配的长度大小(8 字节),这样才可以实时追踪列表空间的使用情况,当空间不足时,及时分配额外空间。

​ 为了减小每次增加 / 删减操作时空间分配的开销,Python 每次分配空间时都会额外多分配一些,这样的机制(over-allocating)保证了其操作的高效性:增加 / 删除的时间复杂度均为 O(1)。

举例:

l = []
l.__sizeof__() # 空列表的存储空间为40字节,在3.7.4版本后空列表是20字节
40
l.append(1)
l.__sizeof__() 
72             # 加入了元素1之后,列表为其分配了可以存储4个元素的空间 (72 - 40)/8 = 4
l.append(2) 
l.__sizeof__()
72             # 由于之前分配了空间,所以加入元素2,列表空间不变
l.append(3)
l.__sizeof__() 
72             # 同上
l.append(4)
l.__sizeof__() 
72             # 同上
l.append(5)
l.__sizeof__() 
104            # 加入元素5之后,列表的空间不足,所以又额外分配了可以存储4个元素的空间
  1. 内置函数

    l = [3, 2, 3, 7, 8, 1]
    l.count(3)  # count(item) 表示统计列表中 item 出现的次数
    2
    l.index(7)  # index(item) 表示返回列表中 item 第一次出现的索引
    3
    l.reverse() # list.reverse() 和 list.sort() 分别表示原地倒转列表和排序
    l
    [1, 8, 7, 3, 2, 3]
    l.sort()
    l
    [1, 2, 3, 3, 7, 8]
    
  2. 性能

​ Python 会在后台,对静态数据做一些资源缓存(resource caching)。通常来说,因为垃圾回收机制的存在,如果一些变量不被使用了,Python 就会回收它们所占用的内存,返还给操作系统,以便其他变量或其他应用使用。

​ 但是对于一些静态变量,比如元组,Python 会暂时缓存这部分内存。这样,下次我们再创建同样大小的元组时,Python 就可以不用再向操作系统发出请求,去寻找内存,而是可以直接分配之前缓存的内存空间,这样就能大大加快程序的运行速度。

举例:

# 计算初始化一个相同元素的列表和元组分别所需的时间
python3 -m timeit 'x=(1,2,3,4,5,6)'
20000000 loops, best of 5: 9.97 nsec per loop
python3 -m timeit 'x=[1,2,3,4,5,6]'
5000000 loops, best of 5: 50.1 nsec per loop
# 索引操作的话,两者的速度差别非常小,几乎可以忽略不计
python3 -m timeit -s 'x=[1,2,3,4,5,6]' 'y=x[3]'
10000000 loops, best of 5: 22.2 nsec per loop
python3 -m timeit -s 'x=(1,2,3,4,5,6)' 'y=x[3]'
10000000 loops, best of 5: 21.9 nsec per loop
  1. 底层实现

​ list 内部实现,使用的是 Array 形式,又因为可变,所以是一个 over-allocate 的 Array。

详细实现,可以参考源码: https://github.com/python/cpython/blob/master/Objects/tupleobject.c

  1. 思考题

    想创建一个空的列表,我们可以用下面的 A、B 两种方式,请问它们在效率上有什么区别吗?我们应该优先考虑使用哪种呢?


# 创建空列表
# option A
empty_list = list()

# option B
empty_list = []

字符串

  1. 字符串基础

字符串是由独立字符组成的一个序列,通常包含在单引号('')双引号("")或者三引号之中(''' '''或""" """,两者一样),比如下面几种写法:

name = 'jason'
city = 'beijing'
text = "hello world"

这里定义了 name、city 和 text 三个变量,都是字符串类型。Python 中单引号、双引号和三引号的字符串是一模一样的,没有区别,比如下面这个例子中的 s1、s2、s3 完全一样:

s1 = 'hello'
s2 = "hello"
s3 = """hello"""
s1 == s2 == s3
True

Python 同时支持这三种表达方式,很重要的一个原因就是,这样方便你在字符串中,内嵌带引号的字符串,比如:

"I'm a student"

Python 的三引号字符串,则主要应用于多行字符串的情境:

def calculate_similarity(item1, item2):
    """
    Calculate similarity between two items
    Args:
        item1: 1st item
        item2: 2nd item
    Returns:
      similarity score between item1 and item2
    """
  1. 字符串操作

切片和遍历:

name = 'jason'
name[0]
'j'
name[1:3]
'as'
for char in name
    print(char)

在 Python 中,字符串通常是不可变的,当尝试修改字符串时,会报 TypeError:

s = 'hello'
s[0] = 'H'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

若要修改字符串,通常需要重新创建一个新的字符串来完成:

s = 'H' + s[1:]
s = s.replace('h', 'H')

但是,在其他语言中,比如 Java,有可变的字符串类型,比如 StringBuilder,每次添加、改变或删除字符(串),无需创建新的字符串,时间复杂度仅为 O(1)。在 Python 初期,想要改变字符串,往往需要 O(n) 的时间复杂度,其中,n 为新字符串的长度。在不断跟新后,Python 增加了加法操作符'+='的字符串拼接。因为它是一个例外,打破了字符串不可变的特性

str1 += str2  # 表示str1 = str1 + str2

# 以下代码,在2.5版本前,时间复杂度是O(n^2)
# 2.5以后版本是O(n
s = ''
for n in range(0, 100000):
    s += str(n)

# 由于列表的 append 操作是 O(1) 复杂度,字符串同理。因此,这个含有 for 循环例子 的时间复杂度为 n*O(1)=O(n)
l = []
for n in range(0, 100000):
    l.append(str(n))
l = ' '.join(l) 

字符串切割,主要适用于数据分析


def query_data(namespace, table):
    """
    given namespace and table, query database to get corresponding
    data         
    """

path = 'hive://ads/training_table'
namespace = path.split('//')[1].split('/')[0] # 返回'ads'
table = path.split('//')[1].split('/')[1] # 返回 'training_table'
data = query_data(namespace, table) 

常见内置函数: string.strip(str),表示去掉首尾的 str 字符串;

string.lstrip(str),表示只去掉开头的 str 字符串;

string.rstrip(str),表示只去掉尾部的 str 字符串。

# 很多时候,从文件读进来的字符串中,开头和结尾都含有空字符,我们需要去掉它们,就可以用 strip() 函数
s = ' my name is jack.zeng '
s.strip()
'my name is jack.zeng'
  1. 字符串格式化 ```python # 第一种 print('no data available for person with id: %s, name: %s' % (id, name))

第二种

print('no data available for person with id: {}, name: {}'.format(id, name))

第三种,3.6 版本之后支持 f-string

print(f"no data available for person with id: {id}, name: {name}")

# 装饰器

1. 基础知识

函数相关

* **在 Python 中,函数是一等公民(first-class citizen),函数也是对象,可以把函数赋予变量:**
```python

def func(message):
    print('Got a message: {}'.format(message))

send_message = func
send_message('hello world')

# 输出
Got a message: hello world
  • 函数当作参数,传入另一个函数中: ```python

def get_message(message):     return 'Got a message: ' + message

def root_call(func, message):     print(func(message))      root_call(get_message, 'hello world')

输出

Got a message: hello world

* **在函数里定义函数,也就是函数的嵌套**:
```python

def func(message):
    def get_message(message):
        print('Got a message: {}'.format(message))
    return get_message(message)

func('hello world')

# 输出
Got a message: hello world
  • 函数的返回值也可以是函数对象(闭包): ```python

def func_closure():     def get_message(message):         print('Got a message: {}'.format(message))     return get_message

send_message = func_closure() send_message('hello world')

输出

Got a message: hello world


Jack 将本帖设为了精华贴 08月16日 21:41
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册