三年前大一第一次接触的编程语言就是Python,达到的巅峰水平就是写一个很多函数扔在一起的文件,后来学编译原理的时候用python写实验报告,然后基本上就是在用C++和JavaScript了。借这个考研复试的机会重学一次,希望能查漏补缺,能深入一些到工程层面更好,来不及就算了。
2022.2.25
看到关于列表的删除添加排序等等,方法很多,从使用方法来看分成两种,一种是面向对象形式的,一种是内置函数形式的。python自动识别定义的变量的类型,定义即声明。lst.sort()这种方法就是调用成员函数了吧,当然有权改动lst的值。sorted(lst)这种调用内置函数的用法,不是引用传递,返回值只是临时变量也说的过去。其实我感觉没必要用内置函数,python的列表那么强大,肯定封装的很好吧,敲一个lst.联想出来的函数一看就懂,后面还有参数。len()内置函数来确定列表项的个数很方便,不过不是还有个类的__len()__方法么,不知道为什么不用这个,猜测和字节长度有关。继续学习观察。小提示,类似list.reverse()这种调用的返回值都是None,如果要获得结果需要先复制列表再对其调用成员函数,至于复制应该如何复制下面会提到。
列表解析,把生成列表的语句塞进列表,非常优雅。写的时候逐步添加东西进去:1、一个列表[]。2、通项公式如2i。3、循环如for i in range(1,3)。最后形式是[2i for i in range(1,3)]。列表切片[起始位置:结束位置:步长]。遵循基本原则有前闭后开(起始位置包括,结束位置不包括)、序号原则(从0开始从前往后递增标记,或者-1开始从后往前递减标记)。所谓列表切片复制都是引用复制,因为列表里存放的是对象的引用,所以什么东西都能塞到列表里。严谨的C++是绝不会容忍此行为的!总之我感觉深拷贝yyds,但是要import copy,没有对象嵌套的时候浅拷贝够用,或者说本来就希望内层指向的是同一个对象。用切片其实就算是浅拷贝。直接赋值就是给对象起别名,over。
元组只要记住是不可修改的(顶层不变性),但是可以整体重新赋值。字典主要记住遍历方式,dict.items(),dict.keys(),dict.values()。总之不知道怎么写就先看成员函数,列出来的东西type一下看看什么类型,然后list转一下,然后直接就遍历呗。python传参是传的对象的引用,知道这个就好办了,显然直接传列表什么的就类似C++的引用,可以修改;但是要记住,任何操作一定要在传入参数的对象上进行,而不是在函数内部做一个复制该对象的复制,那就中计了。如果本意就是希望传复制,那么在传参的时候就手动传一个复制,比如lst[:]一个浅复制。嵌套的可以传深复制。这里体现的python不同于C++的理念,万物皆对象,有点意思,第一遍的时候完全不明白,现在有一点理解了,还需要继续学习。
2.26
函数的传参,带一个*号可以把后续的参数输入都放到元组,带两个*号可以把后续参数放到字典,参数输入当然要有键值对如jian=3。关于定义和传参的合法性检查都做的很好,因此要强调的只有一点,那就是位置一一对应,必须有且仅有一种能成立的匹配参数的方式,否则就会报错。我们很容易举出一些反例,如:
def ttest(*a,**b):
print(a)
print(b)
return
传参只有类似
ttest(1,2,3,key1=1,key2=2)
这样的才能唯一匹配参数。如果出现以下情况:
ttest(key1=1,key2=2,1,2,3)
ttest(1,2,key1=1,3,key2=2)
都是没办法找到一种能把参数放到对应元组或字典的办法的。还有一种是有歧义如:
def ttest(*a,*b):
return
报错多个带一*参数不能定义在一起。
关于导入模块函数的做法:一个.py文件就是一个模块,模块名就是文件名。下面展示一些导入模块函数的写法。假设有一个test.py文件,即test模块,里面有一个ttest函数。
import test
test.ttest()
import test as ts #模块起别名
ts.ttest()
from test import ttest
ttest()
from test import ttest as tst #函数起别名
tst()
from test import * #test模块里的全部函数都能用,但是可能会因为重名而覆盖,谨慎使用
ttest()
python读写文件:
with open('1.txt') as file:#with自动管理文件开闭
content = file.read() #文件对象file包含了一般用的到的所有读文件操作
content = file.readlines() #按行读入,写入列表
with open('1.txt','w') as file: #w是覆盖写模式,a是追加写模式
file.write("write something.")
2.27
查漏补缺做了点题,大部分比较顺利,少部分实现太复杂。原因在于python基于列表、元组、字典、集合等数据结构(?或者叫对象)定义了太多的方法,这些方法翻来覆去可以过滤、排序、重组等,数字与字符串、字符串与列表等都可以迅速地自由转换,加上各种简略的写法推导式,让一个好的思路能大大减少代码量。但是这放在C++的编程环境就不适合,没有那么灵活。看一个例子:
• ⼀个字符串包含若⼲个单词,单词之间通过空格分开
• 两个单词被称为朋友,如果⼀个单词可以通过重新排列另⼀个单词的字符得到,⽐如dealer和leader
• ⼀个单词如果没有朋友,那么它就是孤独的单词
• 找出给定字符串中所有孤独的单词
• ⽐如字符串S = ‘dealer ladder peat leader tape’中孤独的单词就是ladder
使用C++思维,容易想到每一个单词能映射到一个键值对,两个键值对完全一致的单词是朋友。选择一个单词,对剩下的单词循环一次,来寻找其是否有朋友。维护一个数组,初始标记位都是True,每次找到一个朋友,就将这两个单词的状态位都改为False,继续循环。一次循环将会找齐同一类朋友。下次循环将在标记位为True的单词中找。所有循环结束,标记位为True的都是孤独的单词。
Python实现代码如下:
def str2dic(word):
keys = list(set(list(word)))
dic = {k:0 for k in keys}
for i in list(word):
dic[i] += 1
return dic
#True means not lonely
def _islonely(w1,w2):
if len(w1[1]) != len(w2[1]):
return False
dic1 = str2dic(w1[1])
dic2 = str2dic(w2[1])
if dic1 == dic2:
return w1[0], w2[0]
else:
return False
def main():
sstr = input("enter a string:")
res = [[k,True] for k in set(list(sstr.split()))]
for i in range(len(res)-1):
flag = False if not res[i][1] else True
if flag:
for j in range(i+1,len(res)):
loc = _islonely((i,res[i][0]),(j,res[j][0]))
if loc:
res[loc[0]][1] = False
res[loc[1]][1] = False
print("Lonely words are(is):")
for item in res:
if item[1]:
print(item[0],end='\t')
if __name__ == '__main__':
main()
能够继续优化的地方是字符串到字典的映射可以先做好而不是在循环里反复做。
PlanB没有统计每个单词的字母含量,而是直接对其排序,然后把排序结果放入字典,值为1的就是孤独的单词。代码如下:
def planB(msg):
raw_words = msg.split(' ')
words = [''.join(sorted(w)) for w in raw_words]
D = {}
for w in words:
D[w] = D.get(w,0) + 1
lonely_words = [raw_words[words.index(k)] for k,v in D.items() if v == 1]
return lonely_words
代码量少了不少,最重要的就是一个对字符串排序的操作,python直接内置了。题怎么都能做,用python的思路解python的题很重要。
3.1
字符串和正则表达式。正则表达式在re模块中。最常用的函数有:
import re
regex = re.compile(r'\d\d\d\d')
match = regex.search('1234-123455')
match.group()
第一步,创建模式(pattern)对象regex。compile函数的参数是模式字符串,类似一个模板。字符r表示接下来的是原生字符串,所有的字符代表其本来意义。这里只要理解成,这样做省去了编译的时候的转义。比如为了匹配一个反斜杠,需要写一个’\\\\‘的模式。’\\\\'四个反斜杠需要先在编译的时候计算机识别成两个反斜杠,再在compile正则表达式中识别成一个反斜杠。在前面加上r,就只要写两个反斜杠。
search函数只搜索匹配的第一个并且返回一个search的对象,如果没匹配到就返回None。之所以不是直接返回匹配的内容是因为search类还包装了很多别的方法,可以对匹配到的对象进行处理。比如group()方法,直接返回匹配的值;group(k)返回匹配到的第k组,需要在模式进行分组的情况下。
#分组匹配
import re
regex = re.compile(r'(\d\d\d\d)-(\d\d\d\d)')
match = regex.search('1234-1235')
print(match.group())
print(match.group(1))
print(match.groups())
运行结果:
1234-1235
1234
(‘1234’, ‘1235’)
关于模式的管道、规定次数匹配等不详细展开。
如果只想简单拿到所有匹配的字符串,可以在模式对象直接调用findall()
regex.findall('1234-1235')
没有分组,返回匹配字符串列表;有分组,返回匹配元组列表。
模式对象的sub方法,可以将匹配到的内容直接替换。
msg = "<composer>Wolfgang Amadeus Mozart</composer>\n<author>Samuel Beckett</author>\n<city>London</city>"
fdiv = re.compile(r'<([a-zA-Z]+)>')
rdic = re.compile(r'<(/[a-zA-Z]+)>')
msg = fdiv.sub(r'\1: ',msg)
msg = rdic.sub(r'',msg)
print(msg)
输出结果为:
composer: Wolfgang Amadeus Mozart
author: Samuel Beckett
city: London
其中\1表示模式的第一个分组(虽然这里一共就一个分组)。
刚开始写正则主要学会方法,模式怎么写需要反复练习。
字符串的基本的用法:
statement = "Hello hello hhello world wworld STAND POWER THE WORLD!!!"
statement.split() #返回单词列表
statement.split(' ') #指定分隔符
statement.replace('Hello','halle',3) #替换前三次出现的,第三个参数可省略
statement.find('Hello',1,6) #在第1-6的位置寻找Hello,返回第一次出现下标
'World' in statement #真值T or F
','.join(statement.split()) #以新的分隔符将字符列表合并成字符串