本章将首先解释上一章留下的深拷贝&浅拷贝问题,之后讨论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底层|赋值 浅拷贝 深拷贝

将字符复制10次 python 字符串复制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中的循环和分支控制逻辑,也是编程的基础。