易忘易忽略的Python入门知识点

最近在复习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]
-------------完-------------