最近在复习Python的基础知识,发现一些易忽略和忘记的点,这里做一些总结。
可改变 VS 不可改变
不可改变的数据类型包括:数值、字符串和元组;而可改变的数据类型包括:列表、字典、集合。需要注意字典的key必须为不可变类型;集合虽然是可变的但是它要求包含的元素是不可变类型。不可变类型换一种更官方的说法叫可Hashable
,利用可Hashable
的元素,检查成员关系时速度会更快。可通过collections
模块的Hashable
属性查看数据类型是否是不可变的:
import collections
print(isinstance({}, collections.Hashable)) # False
此处引出另外一个点,即isinstance()
和type()
的区别。type()
常用于查看数据类型,isinstance()
常用于判断数据类型;两者对于类的判断会稍有不同,isinstance()
会将子类当作与父类一样的类型,而type()
则认为子类与父类是不同的。
有序 VS 无序
有序的容器:元组、列表;无序的容器:字典、集合。
元组是不可变的有序列表,没有append()
或extend()
方法,也没有remove()
或pop()
方法;字典以键值对的形式存在,而列表只包含值;集合要求元素唯一不可变,列表可重复且可变。
提到append()
或extend()
方法,不得不说下它们之间的区别:extend()
接受一个迭代器(列表、元组、集合、字符串等),一次一个地将迭代器中元素添加到列表中;而append()
直接将迭代器当作一个对象。
容器的定义
列表的定义,元素类型可不同,可嵌套。创建方式:
# 创建空列表
list1 = []
# 创建简单列表
list2 = ['ipine', 7, 'https://ipine.me/']
元组的定义,需要注意空元组和只有一个值的情况:
tup1 = ()
tup2 = (1,)
字典的定义,键唯一且不可变,值不必唯一可取任何数据类型。创建方式如下:
# 创建空字典
dict1 = {}
# 创建简单字典
dict2 = {'name': 'ipine', 'site': 'https://ipine.me/'}
# 使用构造函数 dict()
dict3 = dict([('name','ipine'),('site','https://ipine.me/')])
# 或者
dict4 = dict(name = 'ipine', site = 'https://ipine.me/')
集合的定义,集合的基本功能是删除重复元素和检查成员关系。创建方式如下:
# 创建空集合,不能直接用空花括号,那是创建空字典
set1 = set()
# 创建简单集合
set2 = {'ipine', 'Alex'}
# 或者
set3 = set('ipine', 'Alex')
运算符
1.+
用于连接字符串;*
用于重复输出字符串;字符串在行尾使用反斜杠 \
,表示续行;字符串前面加上字母 r/R
,表示输出原始字符串。
2.is
用于判断两个变量引用的是否是同一个对象,类似于 id(x) == id(y)
,id
用于获取对象内存地址; 而==
用于判断引用变量的值是否相等。
a = [1, 2, 3]
b = a[:]
print(b is a) # False
print(b == a) # True
3.普通的格式化字符串,使用%s
;若Python 3.6+,更好的格式化方法是使用f-strings
def get_name_and_decades(name, age):
return f"My name is {name} and I'm {age / 10:.5f} decades old."
但要注意,如果是输出用户生成的值这种情况下,模板字符串可能是更安全的选择。
from string import Template
def get_name_and_decades(name, age):
a = Template("My name is ${key1} and I'm ${key2 / 10:.5f} decades old.")
a.substitute(key1 = name, key2 = age)
return a
从列表中选择元素
可以使用索引操作符 []
, 索引下标从0开始;获取最后一个元素,传入 -1;切片取多个值,遵循 含前不含后原则:
a[start: end : step] # step默认取1,不会跳过任何元素
a[start : ]
a[ : end]
a[ : ] # 复制列表
从列表中任意选择一个元素
from random import choice
list = ['a','b','c','d']
print(choice(list))
利用索引任意选择一个元素
from random import randrange
randomLetters = ['a','b', 'c', 'd']
randomIndex = randrange(0,len(randomLetters))
print(randomLetters[randomIndex])
列表转其他数据结构
1.将列表转换成字符串: ''.join()
listOfNumbers = [1, 2, 3]
strOfNumbers = ''.join(str(n) for n in listOfNumbers) #对于数字要先转换成str
print(strOfNumbers)
2.列表转元组:tuple()
; 列表转集合:set()
3.列表转字典:zip()
helloWorld = ['hello','world','1','2']
# 转成字典
helloWorldDictionary = dict(zip(helloWorld[0::2], helloWorld[1::2]))
print(helloWorldDictionary) # {'1': '2', 'hello': 'world'}
# 解析成元组对后以列表形式输出
list(zip(helloWorld)) # [('hello',), ('world',), ('1',), ('2',)]
复制列表
方式很多,最常见的是切片方式:newList = oldList[ : ]
;使用内置函数:newList = list(oldList)
;也可以使用库函数,这会涉及到浅拷贝和深拷贝的区分。浅拷贝:newList = copy.copy(oldList)
;对于包含对象的列表,若想将这些对象也完全复制,用深拷贝:copy.deepcopy(oldList)
。
注意:切片方式是浅拷贝。对于浅拷贝,改变对象,原来的列表也会改变
objectList = ['a','b',['ab','ba']]
copiedList = objectList[:]
copiedList[0] = 'c'
copiedList[2][1] = 'd'
print(objectList) # ['a', 'b', ['ab', 'd']]
列表解析的理解
列表解析是一种构建列表的优雅方法,它包含两种形式。
形式一:
[i for i in range(k) if condition]
此时 if
起条件判断作用,满足条件的,将被返回成为最终生成的列表的一员。
形式二:
[i if condition else exp for exp]
此时 if...else
被用来赋值,满足条件的 i
以及 else
被用来生成最终的列表。
举个栗子:
print([i for i in range(10) if i%2 == 0]) # [0, 2, 4, 6, 8]
print([i if i == 0 else 100 for i in range(10)]) # [0, 100, 100, 100, 100, 100, 100, 100, 100, 100]
列表排序
排序会改变原始列表,建议在原始列表不再使用的情况下使用此功能。列表排序有两种方式,一种是列表调用sort
函数,list.sort()
;另一种是往sorted
函数传入列表,sorted(list)
。使用sorted
对复杂列表排序,除了reverse
参数外,还有个key
参数,用于指定按哪个属性值排序,如下示例:
sorted(animals, key=lambda animal: animal['age']) # animals是字典列表
有效利用数据结构
1.移除任何迭代器中的重复值,只需将其传递给内置的 set()
函数。迭代器可以是列表、字典等,查看一个变量是否是迭代器可以应用 .__iter__
。 如果以后需要一个真正的列表,也可以类似地将 set 传递给 list()
函数。
duplicates = [1, 2, 3, 1, 2, 5, 6, 7, 8]
print(list(set(duplicates))) # [1, 2, 3, 5, 6, 7, 8]
注意:使用了
set()
函数后,列表元素的顺序丢失。如果元素的顺序很重要,那么需要使用其他的机制:可参考这个博文
2.使用set存储唯一值。
举个栗子:假装你有一个名为get_random_word()
的函数。 重复调用它以获取1000个随机单词,然后返回包含每个唯一单词的数据结构。
import random
def get_random_word(words):
return random.choice(words)
好的做法:
def get_unique_words(words):
words_set = set()
for _ in range(1000):
words_set.add(get_random_word(words))
return words_set
差的做法:
def get_unique_words(words):
words_set = []
for _ in range(1000):
word = get_random_word(words)
if word not in words_set:
words_set.append(word)
return words_set
这种不好的做法,必须将每个新单词与列表中已有的每个单词进行比较,时间复杂度O(n^2)
;且在列表中检索元素比在集合中慢,集合元素是可 hashable 。集合存储元素的方式允许接近恒定时间检查值是否在集合中,而不像需要线性时间查找的列表。
3.使用 .get()
和 .setdefault()
在字典中定义默认值。
当需要添加,修改或检索可能在字典中或可能不在字典中的项:
name = names.get('name', 'The Man with No Name') # 获取name的值,若name没值就返回后面参数的默认内容如果key存在,则返回对应的值。否则,返回设置的默认值。
但是,如果你仍想在访问name
键时使用默认值更新字典,还是需要再次显式检查该值:
if 'name' not in names:
names['name'] = 'The Man with No Name'
更简洁的做法是使用.setdefault()
,完成以上两个步骤。
name = names.setdefault('name', 'The Man with No Name') # 获取name的值,若name没值就将name的值设置为后面参数的默认内容
标准库的使用
1.使用collections包的 defaultdict()
处理缺少的字典键。为单个键设置默认值时,.get()
和 .setdefault()
可以正常工作,但通常需要为所有可能的未设置键设置默认值,这两种方式比较麻烦。更简洁的方法是使用defaultdict()
方法。
举个栗子:统计字符串中字母出现的次数,数字或是其他符号不算。
常规做法:迭代字符串s,并检查字符是否已经是字典的key,如果不是,则将其添加到字典中,并将1作为默认值;如果是,则key对应值加1:
def count_letter(s):
dic = {}
for ele in s:
if ele.isalpha():
if ele in dic:
dic[ele] +=1
else:
dic[ele] = 1
return dic
更简洁做法:
from collections import defaultdict
def count_letter(s):
dic = defaultdict(int) # int对应值为0
for ele in s:
if ele.isalpha():
dic[ele] += 1
return dic
语法说明,
dict =defaultdict(factory_function)
,factory_function可以是list、set、str等等,作用是当key不存在时,返回的是工厂函数的默认值,比如list对应[ ]
,str对应的是空字符串
,set对应set( )
,int对应0
;
若想设置为某个value;那么可以传入lambda
函数
dic = defaultdict(lambda: 1)
当然,统计迭代器中每个项出现的次数,可以直接使用 Counter()
函数;
若好奇最常见的一个项是什么,只需使用 .most_common()
:
from collections import Counter
list = ["a","b","b"]
counts = Counter(list) # Counter({'a': 1, 'b': 2})
counts.most_common(1) # b
2.使用collections包的deque()
方法创建队列和栈数据结构。队列,先入先出的结构;栈,后入先出的结构,类似于浏览器的“后退”按钮。
定义队列,入队和出队操作:
from collections import deque
queue = deque([1, 3, 4, 7, 0])
queue.append(10)
queue.popleft()
定义栈,入栈和出栈操作:
stack = deque(['1st webpage','2nd webpage','3rd webpage'])
stack.append('4th webpage')
stack.pop()
3.使用collections包的排序字典方法 OrderedDict()
,使输出的字典顺序与key的输入顺序一致。
from collections import OrderedDict
order_dict = OrderedDict({'first':1, 'second':2, 'third': 3})
4.使用 itertool 生成排列和组合: itertools.permutations()
和 itertools.combinations()
。
排列是考虑了顺序的,(A,B)和(B,A)不一样;组合是没考虑顺序,(A,B)和(B,A)一样。
import itertools
friends = ['A', 'B', 'C', 'D']
list(itertools.permutations(friends, r=2)) # r指定每个分组有几个值
list(itertools.combinations(friends, r=2))
5.使用operator模块中的 add()
方法,更机智地对列表元素求和。
from operator import add
list(map(add, list1, list2)) # map每个元素;最后记得使用 list()打印 map()函数的结果
对两个列表的元素求和,也可以在列表解析中使用 zip()
函数
[sum(x) for x in zip(list1, list2)]
6.使用functools包的 reduce()
方法,将嵌套列表变成flat list。
from functools import reduce
listOfList = [[1,2], [3,4], [5,6]]
print(reduce(lambda x,y: x+y,listOfLists)) # reduce需传入两个参数,前一个是lambda表达式,后一个是列表; +操作符是连接列表的符号
也可以使用 sum()
函数:
list = [[1,2],[3,4],[5,6]]
sum(list, []) # [1, 2, 3, 4, 5, 6]