python进阶

1,学习路径

 

2,安装Jupyter Notebook

3,列表和元组基础

在绝大多数编程语言中,集合的数据类型必须一致。不过,对于 Python 的列表和元组来说,并无此要求

 

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

l

[1, 2, 'hello', 'world']

tup = ('jason', 22) # 元组中同时含有int和string类型的元素

tup

('jason', 22)

 

列表是动态的,长度大小不固定,可以随意地增加、删减或者改变元素(mutable)。而元组是静态的,长度大小固定,无法增加删减或者改变(immutable)。

 

 

改变元祖只能重新开辟一块内存,创建新的元组

tup = (1, 2, 3, 4)

new_tup = tup + (5, ) # 创建新的元组new_tup,并依次填充原元组的值

new _tup

(1, 2, 3, 4, 5)

 

l = [1, 2, 3, 4]

l.append(5) # 添加元素5到原列表的末尾

l

[1, 2, 3, 4, 5]

 

 

Python 中的列表和元组都支持负数索引

列表和元组都支持切片操作

 

l = [1, 2, 3, 4]

l[1:3] # 返回列表中索引从1到2的子列表

[2, 3]

 

tup = (1, 2, 3, 4)

tup[1:3] # 返回元组中索引从1到2的子元组

(2, 3)

 

 

列表和元组都可以随意嵌套

 

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

tup = ((1, 2, 3), (4, 5, 6)) # 元组的每一个元素也是一个元组

 

可以通过 list() 和 tuple() 函数相互转换

list((1, 2, 3))

[1, 2, 3]

 

tuple([1, 2, 3])

(1, 2, 3)

 

列表和元组常用的内置函数

 

l = [3, 2, 3, 7, 8, 1]

l.count(3)

2

l.index(7)

3

l.reverse()

l

[1, 8, 7, 3, 2, 3]

l.sort()

l

[1, 2, 3, 3, 7, 8]

 

tup = (3, 2, 3, 7, 8, 1)

tup.count(3)

2

tup.index(7)

3

list(reversed(tup))

[1, 8, 7, 3, 2, 3]

sorted(tup)

[1, 2, 3, 3, 7, 8]

 

 

列表和元组存储方式的差异

列表和元组最重要的区别就是,列表是动态的、可变的,而元组是静态的、不可变的。这样的差异,势必会影响两者存储方式

 

l = [1, 2, 3]

l.__sizeof__()

64

 

tup = (1, 2, 3)

tup.__sizeof__()

48

 

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

 

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

 

元组长度大小固定,元素不可变,所以存储空间固定

 

列表和元组的性能

元组要比列表更加轻量级一些,所以总体上来说,元组的性能速度要略优于列表

 

如果你想要增加、删减或者改变元素,那么列表显然更优

 

列表和元组的使用场景

 

如果存储的数据和数量不变,比如你有一个函数,需要返回的是一个地点的经纬度,然后直接传给前端渲染,那么肯定选用元组更合适。

 

def get_location():

    .....

return (longitude, latitude)

 

如果存储的数据或数量是可变的,比如社交平台上的一个日志功能,是统计一个用户在一周之内看了哪些用户的帖子,那么则用列表更合适。

 

viewer_owner_id_list = [] # 里面的每个元素记录了这个viewer一周内看过的所有owner的id

records = queryDB(viewer_id) # 索引数据库,拿到某个viewer一周内的日志

for record in records:

viewer_owner_id_list.append(record.id)

4,字典、集合

字典是一系列由键(key)和值(value)配对组成的元素的集合

在 Python3.7+,字典被确定为有序(注意:在 3.6 中,字典有序是一个 implementation detail,在 3.7 才正式成为语言特性,因此 3.6 中无法 100% 确保其有序性),而 3.6 之前是无序的,其长度大小可变,元素可以任意地删减和改变

 

Python 中字典和集合,无论是键还是值,都可以是混合类型

 

d1 = {'name': 'jason', 'age': 20, 'gender': 'male'}

d2 = dict({'name': 'jason', 'age': 20, 'gender': 'male'})

d3 = dict([('name', 'jason'), ('age', 20), ('gender', 'male')])

d4 = dict(name='jason', age=20, gender='male')

d1 == d2 == d3 ==d4

True

 

s1 = {1, 2, 3}

s2 = set([1, 2, 3])

s1 == s2

True

典访问可以直接索引键,如果不存在,就会抛出异常

d = {'name': 'jason', 'age': 20}

d['name']

'jason'

d['location']

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

KeyError: 'location'

 

也可以使用 get(key, default) 函数来进行索引。如果键不存在,调用 get() 函数可以返回一个默认值

 

d = {'name': 'jason', 'age': 20}

d.get('name')

'jason'

d.get('location', 'null')

'null'

 

集合并不支持索引操作,因为集合本质上是一个哈希表,和列表不一样

 

s = {1, 2, 3}

s[0]

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: 'set' object does not support indexing

 

想要判断一个元素在不在字典或集合内,我们可以用 value in dict/set 来判断

 

s = {1, 2, 3}

1 in s

True

10 in s

False

 

d = {'name': 'jason', 'age': 20}

'name' in d

True

'location' in d

False

 

字典和集合也同样支持增加、删除、更新等操作

 

d = {'name': 'jason', 'age': 20}

d['gender'] = 'male' # 增加元素对'gender': 'male'

d['dob'] = '1999-02-01' # 增加元素对'dob': '1999-02-01'

d

{'name': 'jason', 'age': 20, 'gender': 'male', 'dob': '1999-02-01'}

d['dob'] = '1998-01-01' # 更新键'dob'对应的值

d.pop('dob') # 删除键为'dob'的元素对

'1998-01-01'

d

{'name': 'jason', 'age': 20, 'gender': 'male'}

 

s = {1, 2, 3}

s.add(4) # 增加元素4到集合

s

{1, 2, 3, 4}

s.remove(4) # 从集合中删除元素4

s

{1, 2, 3}

 

注意: 集合的 pop() 操作是删除集合中最后一个元素,可是集合本身是无序的,你无法知道会删除哪个元素,因此这个操作得谨慎使用

 

对于字典,通常会根据键或值,进行升序或降序排序

 

d = {'b': 1, 'a': 2, 'c': 10}

d_sorted_by_key = sorted(d.items(), key=lambda x: x[0]) # 根据字典键的升序排序

d_sorted_by_value = sorted(d.items(), key=lambda x: x[1]) # 根据字典值的升序排序

d_sorted_by_key

[('a', 2), ('b', 1), ('c', 10)]

d_sorted_by_value

[('b', 1), ('a', 2), ('c', 10)]

 

集合,其排序和前面讲过的列表、元组很类似,直接调用 sorted(set) 即可

s = {3, 4, 2, 1}

sorted(s) # 对集合的元素进行升序排序

[1, 2, 3, 4]

 

字典和集合是进行过性能高度优化的数据结构,特别是对于查找、添加和删除操作

为了提高存储空间的利用率,现在的哈希表除了字典本身的结构,会把索引和哈希值、键、值单独分开

 

插入位置计算

每次向字典或集合插入一个元素时,Python 会首先计算键的哈希值(hash(key)),再和 mask = PyDicMinSize - 1 做与操作,计算这个元素应该插入哈希表的位置 index = hash(key) & mask。如果哈希表中此位置是空的,那么这个元素就会被插入其中。而如果此位置已被占用,Python 便会比较两个元素的哈希值和键是否相等。若两者都相等,则表明这个元素已经存在,如果值不同,则更新值。若两者中有一个不相等,这种情况我们通常称为哈希冲突(hash collision),意思是两个元素的键不相等,但是哈希值相等。这种情况下,Python 便会继续寻找表中空余的位置,直到找到位置为止。

 

元素查询

和前面的插入操作类似,Python 会根据哈希值,找到其应该处于的位置;然后,比较哈希表这个位置中元素的哈希值和键,与需要查找的元素是否相等。如果相等,则直接返回;如果不等,则继续查找,直到找到空位或者抛出异常为止。

 

删除元素

对于删除操作,Python 会暂时对这个位置的元素,赋于一个特殊的值,等到重新调整哈希表的大小时,再将其删除。不难理解,哈希冲突的发生,往往会降低字典和集合操作的速度。因此,为了保证其高效性,字典和集合内的哈希表,通常会保证其至少留有 1/3 的剩余空间。随着元素的不停插入,当剩余空间小于 1/3 时,Python 会重新获取更大的内存空间,扩充哈希表。不过,这种情况下,表内所有的元素位置都会被重新排放。虽然哈希冲突和哈希表大小的调整,都会导致速度减缓,但是这种情况发生的次数极少。所以,平均情况下,这仍能保证插入、查找和删除的时间复杂度为 O(1)。

5,字符串

字符串是由独立字符组成的一个序列,通常包含在单引号('')双引号("")或者三引号之中(''' '''或""" """,两者一样)

 

s1 = 'hello'

s2 = "hello"

s3 = """hello"""

s1 == s2 == s3

True

 

"I'm a student"

Python 的三引号字符串,则主要应用于多行字符串的情境,比如函数的注释等等

 

 

 

Python 也支持转义字符

 

 

字符串的常用操作

Python 的字符串同样支持索引,切片和遍历等等操作

Python 的字符串是不可变的(immutable)

 

s = 'hello'

s[0] = 'H'

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

TypeError: 'str' object does not support item assignment

 

Python 中字符串的改变,通常只能通过创建新的字符串来完成

 

s = 'H' + s[1:]

s = s.replace('h', 'H')

 

自从 Python2.5 开始,每次处理字符串的拼接操作时(str1 += str2),Python 首先会检测 str1 还有没有其他的引用。如果没有的话,就会尝试原地扩充字符串 buffer 的大小,而不是重新分配一块内存来创建新的字符串并拷贝。这样的话,上述例子中的时间复杂度就仅为 O(n)

 

使用字符串内置的 join 函数。string.join(iterable),表示把每个元素都按照指定的格式连接起来

 

string.split(separator) 表示把字符串按照 separator 分割成子字符串

string.strip(str),表示去掉首尾的 str 字符串;

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

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

string.find(sub, start, end),表示从 start 到 end 查找字符串中子字符串 sub 的位置

 

string.format(),就是所谓的格式化函数

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

 

在 Python 之前版本中,字符串格式化通常用 % 来表示

print('no data available for person with id: %s, name: %s' % (id, name))

 

拼接性能比较

s = ''

for n in range(0, 100000):

    s += str(n)

 

 

l = []

for n in range(0, 100000):

    l.append(str(n))

    

s = ' '.join(l)

 

如果字符串拼接的次数较少,比如range(100),那么方法一更优,因为时间复杂度精确的来说第一种是O(n),第二种是O(2n),如果拼接的次数较多,比如range(1000000),方法二稍快一些,虽然方法二会遍历两次,但是join的速度其实很快,列表append和join的开销要比字符串+=小一些

6,输入与输出

name = input('your name:')

gender = input('you are a boy?(y/n)')

 

###### 输入 ######

your name:Jack

you are a boy?

 

welcome_str = 'Welcome to the matrix {prefix} {name}.'

welcome_dic = {

    'prefix': 'Mr.' if gender == 'y' else 'Mrs',

    'name': name

}

 

print('authorizing...')

print(welcome_str.format(**welcome_dic))

 

########## 输出 ##########

authorizing...

Welcome to the matrix Mr. Jack.

 

 

a = input()

1

b = input()

2

 

print('a + b = {}'.format(a + b))

########## 输出 ##############

a + b = 12

print('type of a is {}, type of b is {}'.format(type(a), type(b)))

########## 输出 ##############

type of a is <class 'str'>, type of b is <class 'str'>

print('a + b = {}'.format(int(a) + int(b)))

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星月IWJ

曾梦想杖键走天涯,如今加班又挨

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值