本章将首先解释上一章留下的深拷贝&浅拷贝问题,之后讨论python中字符串及其使用和正则表达式。
深拷贝&浅拷贝
用简单的话来概括就是:
浅拷贝:一件衣服两个人换着穿,最终还是只有一件衣服。
深拷贝:两人买了两件一样的衣服,最终有两件衣服。
对一个对象进行浅拷贝其实是新创建了一个类型跟原对象一样,其内容是原来对象元素的引用,换句话说就是,这个拷贝的对象是新的,但是它的内容不是。浅拷贝可以通过以下几种方式实现:
- 完全切片操作[:]
- list(),dict()
- copy模块的copy()函数
person=['name',['savings',100.0]]
hubby=person[:]
wife=list(person)
print(id(hubby))
print(id(wife))
hubby[0]='husband'
hubby[1][1]=50.00
print(hubby) #[1, ['savings', 50.0]]
print(wife) #['name', ['savings', 50.0]]
对于上面的代码结果,可以发现在更改第一项name的时候,wife中的name并没有发生改变,这是因为字符串是不可变类型,在发生浅拷贝时,字符串被显式的拷贝,并创建了一个新的字符串对象。而在对第二项列表进行操作时,wife跟着hubby一起改变了,这是因为列表的拷贝只是将其引用进行了复制。这一点可以通过查看两个列表元素的id求证。
此处关于直接等号赋值和浅拷贝之间可能会混淆,直接赋值后新的引用是与旧引用相同(id相同),而浅拷贝则是创建了新的引用。下面的图表示的很清楚:(ref:Python底层|赋值 浅拷贝 深拷贝)
要得到原对象的深拷贝需要创建一个新的容器对象,包含原有对象元素全新拷贝的引用需要copy.deepcopy()函数。
import copy #此模块中只有两个函数copy()和deepcopy()
person=['name',['savings',100.0]]
hubby=copy.deepcopy(person)
wifey=copy.deepcopy(person)
print(id(person)) #9913568
print(id(hubby)) #9914488
print(id(wifey)) #46703000
print(id(person[1])) #46702960
print(id(hubby[1])) #9913728
print(id(wifey[1])) #46702800
首先通过id()函数确认深拷贝得到的结果是与原对象不同的,并且列表中第二个元素列表的id也都各不相同。
字符串
str字符串
字符串类型是python中最为常见的类型,我们可以使用单引号或双引号来表示字符串,在python中两种引号的作用是相通的,这一点不同于其他脚本语言。字符串是不可变类型,就是说改变一个字符串中的元素就需要新建一个字符串,字符串由独立的字符组成,并且这些字符可以通过切片操作进行访问。
python中字符串由str和Unicode两种,他们都是抽象类basestring的子类,basestring是不能实例化的。
字符串的创建:
s=str(“hello”)
s2=“hello”
访问字符串中的元素:
s[0]
s[1:5]
改变字符串:
s=s+s2
跟数字类型一样,字符串类型也是不可改变的,所以如果要对字符串进行修改就要通过新创建一个字符串来实现。不能仅仅只改变或删除一个字符。
字符串删除:
del s
s=’’
字符串的操作符:
- 字符串的比较运算是按照ASCII值得大小来比较的。
- 切片运算符
- 成员操作符:in 、not in
- 连接运算符:+
- 可以通过单双引号混用来实现将一个字符串分为子串。
- 重复操作符*
- 格式化字符串%:
- %c
- ‘%.2f’ % 1234.567890
- “we have %d apples” % 10
- ‘your host is %s’ % ‘here’
- ‘port number: %d’ % 80
字符串模板:格式化操作符可以用来将字符串中某些部分替代为变量或值,但是偶尔会出现遗漏转换类型符号的错误,python中有一种更为简单的方式实现格式化字符串。使用时需要导入从模块string导入Template模块:
from string import Template
# 通过使用类似shell scripts中表示变量的方法创建字符串,即美元符%加上花括号。
s=Template('There is ${number} dogs')
# 使用safe_substitute()函数,对格式化字符串中的变量进行赋值。
print(s.safe_substitute(number=5))
字符串的标准类型函数:
- len()
- min()/max()
- enumerate():迭代字符串中的每位字符。
- zip(s,t):将两个字符串中的对应部分打包成元组。
字符串的内建函数:数量过多,这里列举部分常用的
- string.capitalize():把字符串第一个字符大写
- string.count(str, beg=0, end=len(string)):返回str在string中出现的次数,可以指定范围,默认为整个字符串。
- string.decode(encoding=‘UTF-8’,errors=‘strict’):以指定的编码方式解码string。
- string.find(str, beg=0, end=len(string)):查找str是否在string中,返回-1或index。
- string.index(str, beg=0, end=len(string)):与find方法类似。
- string.isalnum():是否至少有一个字符并且所有字符都是数字或字母。
- string.isalpha():是否至少有一个字符并且所有字符都是字母。
- string.isdigit():是否至少有一个字符并且所有字符都是数字
- string.islower():是否至少有一个字母字符并且所有字符都是小写。
- string.lower():转换所有大写字符为小写。
- string.upper():转换所有小写字符为大写。
- string.replace(str1.str2.,num=string.count(str1)):把string中str1替换为str2,可以指定替换的次数。
- string.startswith(obj, beg=0, end=len(string)):检查字符串是否以obj开头。
- string.title():将每个单词首字符转为大写,像是标题格式。
字符串中的一些特殊规定:
- 和C/C++一样,用反斜杠\来表示转义,例如\n表示换行符
- 与C/C++不同的是,python字符串并不以\0作为结束符 (ref:python的转义字符是什么)
- 三引号会在与Html或SQL相结合是用到。
Unicode字符串
Unicode字符串:通过在字符串引号前加u或者U来表示Unicode字符串
这里提一下Unicode和ASCII码:
- ASCII码首次发表于1967年,计算机中1个字节等于8比特,所以一个字节所能表示的最大整数就是255,0-255就被用来表示一些数字,字符,符号。ASCII
- Unicode又称为万国码,于1994年发布,由于在世界范围例如中文、日文、韩文等语言使用不同于英文字符的方式,Unicode通常使用两个字节来表示一个字符,unicode是字符集,而utf-8、utf-16等是编码规则。
Codec是COder/DECoder的组合,定义了文本与二进制的转换方式。对于ASCII编码,这种转换相对简单,0~255分别对应各个字符。而对于使用多字节Unicode,就出现了多种不同的编码方式,例如utf-8、utf-16。
utf-8:同时支持用一个字节来编码ASCII字符,这使得同时处理ASCII和Unicode文本工作变得轻松。utf-8可以用1个到4个字节来表示其他语言。utf-8是Unicode的实现方式之一。utf-8的编码规则如下:
- 对于单字节,字节的第一位(字节的最高位)设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
- 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
utf-16把所有字符都是用两个字节来存储。
正则表达式
正则表达式(regular expression)为高级文本匹配,以及搜索-替代等功能提供了基础。正则表达式是一些有字符和特殊符号组成的字符串,他们描述了这字符和字符的某种重复方式,一个正则表达式可以匹配多个字符串。
python通过标准库的re模块支持正则表达式。
正则表达式中的特殊符号与字符
符号 | 说明 | 例子 |
---|---|---|
re1|re2 | 匹配正则表达式re1或re2 | foo|bar (foo,bar) |
– | – | – |
. | 匹配任何字符串(换行符除外) | b.b(b1b,bab…) |
– | – | – |
^ | 匹配字符串的开始 | ^Dear (匹配任何以Dear开始的字符串) |
– | – | – |
$ | 匹配字符串的结尾 | /bin/*sh$(匹配任何以/bin/*sh接触的字符串) |
– | – | – |
* | 匹配前面出现的正则表达式零次或多次 | [A-za-z0-9]* |
– | – | – |
+ | 匹配前面出现的正则表达式一次或多次 | [a-z]+\.com |
– | – | – |
? | 匹配前面出现的正则表达式一次或零次 | goo? |
– | – | – |
{N} | 匹配前面出现的正则表达式N此 | [0-9]{3} |
– | – | – |
{M,N} | 匹配重复出现M到N词的正则表达式 | [0-9]{5,9} |
– | – | – |
[…] | 匹配字符组里出现的任意一个字符 | [aeiou] |
– | – | – |
[…x-y…] | 匹配从x到y中的任意一个字符 | [0-9],[a-zA-z] |
– | – | – |
[^…] | 不匹配此字符集出现的任何一个字符,包括某一范围的字符 | [^aeiou](在方括号内第一位出现^就表示不匹配) |
– | – | – |
(…) | 匹配封闭括号中正则表达式,并保存为自组 | \d+(\.\d*)?(表示浮点数) |
– | – | – |
(*|+|?|{})? | 用于上面出现的任何非贪婪版本重复匹配次数符号(*,+,?,{}) | .*?[a-z] |
– | – | – |
\d | 匹配任何数字,和[0-9]一样(\D是\d的反义) | data\d+.txt |
– | – | – |
\w | 匹配任何数字字母字符,和[A-za-z0-9]相同(\W是反义) | [A-Za-z_]\w+ |
– | – | – |
\s | 匹配任何空白字符(\S是反义) | of\sthe |
– | – | – |
\b | 匹配单词的边界(\B是反义) | \bThe\b(仅匹配The) |
– | – | – |
\nn | 匹配已保存的子组 | |
– | – | – |
\c | 注意匹配特殊字符c | |
– | – | – |
\A(\Z) | 匹配字符串的起始(结束) | \ADear |
python中的正则表达式
re模块核心函数:
- compile(pattern, flags=0):对一个正则表达式模式pattern进行编译,返回一个regex对象。
- match(pattern, string, flags=0):常使用正则表达式pattern匹配字符串string,匹配成功,则返回一个匹配对象。
- search(pattern, string, flags=0):在字符串中查找正则表达式pattern的第一次出现,匹配成功,则返回一个匹配对象。
- findall(pattern, string[,flags]):在string中查找pattern的所有非重复出现,返回匹配对象的列表。
- finditer(pattern, string[,flags]):和findall相同,返回迭代器。
- split(pattern, string, max=0):根据pattern中的分隔符把字符string分割为一个列表,返回成功匹配的列表。
- sub(pattern, repl, string, max=0):把字符串string中所有匹配正则表达式pattern的地方替换成repl、
- group(num=0):返回全部匹配对象。
import re
# match()
m=re.match('[a-z]oo','foo') #匹配模式与字符串,这里的m是匹配对象的实例<re.Match object; span=(0, 3), match='foo'>
print(m.group()) #grou()将会得到匹配的对象
# match()是从字符串的首部开始匹配,所以foo和seafood将无法用match()匹配
# search()
m=re.search('foo','seafood')
print(m.group()) #foo
# 匹配对个字符串(|)
bt='bat|bet|bit|but'
m=re.match(bt,'but why')
print(m.group()) #but
# 匹配任意单个字符(.)
bt='b.t'
m=re.match(bt,'but')
print(m.group()) #but
bt='3\.14' #匹配小数时记得加上转义符号
m=re.match(bt,'3.1415')
print(m.group())
# 创建字符集合
bt='[cr][23][dp][ax]'
m=re.match(bt,'c3pa')
print(m.group()) #c3pa
# 重复、特殊字符和子组:匹配yyy@www.xxx.com
bt='\w+@w{3}\.[A-Za-z0-9]+\.com'
m=re.match(bt,'taohan@www.163.com')
print(m.group()) #匹配成功
# 边界匹配
bt='^the'
m=re.search(bt,'the apple')
print(m.group())
# 用sub()进行搜索和替换:第二个参数替换第一个参数
m2=re.sub('x','mr.smith','dear x') #dear mr.smith
print(m2)
# 用split()分割
re.split(':', 'str1:str2:str3') #['str1', 'str2', 'str3']
正则表达式示例程序
# 1.py
# 用一段代码随机生成网址写入到一个文件
import re
from random import randint, choice
from string import ascii_lowercase
doms=['.com','.edu','.net','.org','.gov']
# 网址举例:www.baidu.com
addr=[]
for i in range(0,5):
str='www.'
# 随机长度的中间段
medium=randint(5,7)
for i in range(0,medium):
# 从小写字母中随机挑选字母
c=choice(ascii_lowercase)
str+=c
dom=randint(0,4)
str+=doms[dom]
addr.append(str)
print(addr)
fname='file2'
fobj=open(fname,'w')
for a in addr:
fobj.writelines(a+'\n')
fobj.close()
# 2.py
# 从file2文件中读取每一行,提取中域名中间段以及域名所属类型,暂时没写完
import re
addr=[]
fname='file2'
fobj=open(fname,'r')
for line in fobj :
medium=re.search('\.[a-z]+\.',line)
dom=re.search('.com|.edu|.net|.org|.gov',line)
print(dom.group())
print(medium.group())
下一章将讨论python中的循环和分支控制逻辑,也是编程的基础。