全网目前最全python例子(附源码)第二章:Python之坑,第三章:Python字符串和正则...

二 Python之坑

1 含单个元素的元组

Python中有些函数的参数类型为元组,其内有1个元素,这样创建是错误的:

c = (5) # NO!

它实际创建一个整型元素5,必须要在元素后加一个逗号:

c = (5,) # YES!

2 默认参数设为空

含有默认参数的函数,如果类型为容器,且设置为空:

def f(a,b=[]):  # NO!

    print(b)

    return b

 

ret = f(1)

ret.append(1)

ret.append(2)# 当再调用f(1)时,预计打印为 []

f(1)# 但是却为 [1,2]

这是可变类型的默认参数之坑,请务必设置此类默认参数为None:

def f(a,b=None): # YES!

    pass

3 共享变量未绑定之坑

有时想要多个函数共享一个全局变量,但却在某个函数内试图修改它为局部变量:

i = 1def f():

    i+=1 #NO!

    def g():

    print(i)

应该在f函数内显示声明i为global变量:

i = 1def f():

    global i # YES!

    i+=1

4 lambda自由参数之坑

排序和分组的key函数常使用lambda,表达更加简洁,但是有个坑新手容易掉进去:

a = [lambda x: x+i for i in range(3)] # NO!for f in a:

    print(f(1))# 你可能期望输出:1,2,3

但是实际却输出: 3,3,3. 定义lambda使用的i被称为自由参数,它只在调用lambda函数时,值才被真正确定下来,这就犹如下面打印出2,你肯定确信无疑吧。

a = 0

a = 1

a = 2def f(a):

    print(a)

正确做法是转化自由参数为lambda函数的默认参数:

a = [lambda x,i=i: x+i for i in range(3)] # YES!

5 分不清是默认参数还是关键字参数

定义函数f,在使用它时,width的以下三种写法都是OK的,如果未了解函数原型,容易分不清width到底是位置参数还是关键字参数。

def f(a,width=10.0):

    print(width)

 

f(1,width=15.0) # 15.0  # 容易分不清width是位置参数还是关键字参数

f(1,15.0) # 15.0

f(1) # 10.0

width是带默认参数的位置参数,关键字参数必须使用两个星号声明。因此如果要区分它们,需要关注函数的定义。

def f(a,**b):

    print(b)

f(1,width=15.0) # width是关键字参数,不是默认参数

6 列表删除之坑

删除一个列表中的元素,此元素可能在列表中重复多次:

def del_item(lst,e):

    return [lst.remove(i) for i in e if i==e] # NO!

考虑删除这个序列[1,3,3,3,5]中的元素3,结果发现只删除其中两个:

del_item([1,3,3,3,5],3) # 结果:[1,3,5]

正确做法:

def del_item(lst,e):

    d = dict(zip(range(len(lst)),lst)) # YES! 构造字典

    return [v for k,v in d.items() if v!=e]

 

7 列表快速复制之坑

在python中*与列表操作,实现快速元素复制:

a = [1,3,5] * 3 # [1,3,5,1,3,5,1,3,5]

a[0] = 10 # [10, 2, 3, 1, 2, 3, 1, 2, 3]

如果列表元素为列表或字典等复合类型:

a = [[1,3,5],[2,4]] * 3 # [[1, 3, 5], [2, 4], [1, 3, 5], [2, 4], [1, 3, 5], [2, 4]]

 

a[0][0] = 10 #  

结果可能出乎你的意料,其他a[1[0]等也被修改为10

[[10, 3, 5], [2, 4], [10, 3, 5], [2, 4], [10, 3, 5], [2, 4]]

这是因为*复制的复合对象都是浅引用,也就是说id(a[0])与id(a[2])门牌号是相等的。如果想要实现深复制效果,这么做:

a = [[] for _ in range(3)]

8 字符串驻留

In [1]: a = 'something'

    ...: b = 'some'+'thing'

    ...: id(a)==id(b)

Out[1]: True

如果上面例子返回True,但是下面例子为什么是False:

In [1]: a = '@zglg.com'

 

In [2]: b = '@zglg'+'.com'

 

In [3]: id(a)==id(b)

Out[3]: False

这与Cpython 编译优化相关,行为称为字符串驻留,但驻留的字符串中只包含字母,数字或下划线。

9 相同值的不可变对象

In [5]: d = {}

    ...: d[1] = 'java'

    ...: d[1.0] = 'python'

 

In [6]: d

Out[6]: {1: 'python'}

### key=1,value=java的键值对神器消失了

In [7]: d[1]

Out[7]: 'python'

In [8]: d[1.0]

Out[8]: 'python'

这是因为具有相同值的不可变对象在Python中始终具有相同的哈希值

由于存在哈希冲突,不同值的对象也可能具有相同的哈希值。

10 对象销毁顺序

创建一个类SE:

class SE(object):

  def __init__(self):

    print('init')

  def __del__(self):

    print('del')

创建两个SE实例,使用is判断:

In [63]: SE() is SE()

init

initdeldel

Out[63]: False

 

创建两个SE实例,使用id判断:

In [64]: id(SE()) == id(SE())

initdel

initdel

Out[64]: True

调用id函数, Python 创建一个 SE 类的实例,并使用id函数获得内存地址后,销毁内存丢弃这个对象。

当连续两次进行此操作, Python会将相同的内存地址分配给第二个对象,所以两个对象的id值是相同的.

但是is行为却与之不同,通过打印顺序就可以看到。

11 充分认识for

In [65]: for i in range(5):

    ...:   print(i)

    ...:   i = 1001234

为什么不是执行一次就退出?

按照for在Python中的工作方式, i = 10 并不会影响循环。range(5)生成的下一个元素就被解包,并赋值给目标列表的变量i.

12 认识执行时机

array = [1, 3, 5]

g = (x for x in array if array.count(x) > 0)

g为生成器,list(g)后返回[1,3,5],因为每个元素肯定至少都出现一次。所以这个结果这不足为奇。但是,请看下例:

array = [1, 3, 5]

g = (x for x in array if array.count(x) > 0)

array = [5, 7, 9]

请问,list(g)等于多少?这不是和上面那个例子结果一样吗,结果也是[1,3,5],但是:

In [74]: list(g)

Out[74]: [5]

这有些不可思议~~ 原因在于:

生成器表达式中, in 子句在声明时执行, 而条件子句则是在运行时执行。

所以代码:

array = [1, 3, 5]

g = (x for x in array if array.count(x) > 0)

array = [5, 7, 9]

等价于:

g = (x for x in [1,3,5] if [5,7,9].count(x) > 0)

正在陆续汇总更多Python使用之坑 ...

三、Python字符串和正则

字符串无所不在,字符串的处理也是最常见的操作。本章节将总结和字符串处理相关的一切操作。主要包括基本的字符串操作;高级字符串操作之正则。目前共有16个小例子

1 反转字符串

st="python"#方法1''.join(reversed(st))#方法2

st[::-1]

2 字符串切片操作

字符串切片操作——查找替换3或5的倍数

In [1]:[str("java"[i%3*4:]+"python"[i%5*6:] or i) for i in range(1,15)]

OUT[1]:['1',

 '2',

 'java',

 '4',

 'python',

 'java',

 '7',

 '8',

 'java',

 'python',

 '11',

 'java',

 '13',

 '14']

3 join串联字符串

In [4]: mystr = ['1',

   ...:  '2',

   ...:  'java',

   ...:  '4',

   ...:  'python',

   ...:  'java',

   ...:  '7',

   ...:  '8',

   ...:  'java',

   ...:  'python',

   ...:  '11',

   ...:  'java',

   ...:  '13',

   ...:  '14']

 

In [5]: ','.join(mystr) #用逗号连接字符串

Out[5]: '1,2,java,4,python,java,7,8,java,python,11,java,13,14'

4 字符串的字节长度

def str_byte_len(mystr):

    return (len(mystr.encode('utf-8')))

 

 

str_byte_len('i love python')  # 13(个字节)

str_byte_len('字符')  # 6(个字节)

以下是正则部分

import re

1 查找第一个匹配串

s = 'i love python very much'

pat = 'python' 

r = re.search(pat,s)

print(r.span()) #(7,13)

2 查找所有1的索引

s = '山东省潍坊市青州第1中学高三1班'

pat = '1'

r = re.finditer(pat,s)for i in r:

    print(i)

# <re.Match object; span=(9, 10), match='1'># <re.Match object; span=(14, 15), match='1'>

3 \d 匹配数字[0-9]

findall找出全部位置的所有匹配

s = '一共20行代码运行时间13.59s'

pat = r'\d+' # +表示匹配数字(\d表示数字的通用字符)1次或多次

r = re.findall(pat,s)

print(r)# ['20', '13', '59']

4 匹配浮点数

?表示前一个字符匹配0或1次

s = '一共20行代码运行时间13.59s'

pat = r'\d+\.?\d+' # ?表示匹配小数点(\.)0次或1次

r = re.findall(pat,s)

print(r)# ['20', '13.59']

5 ^匹配字符串的开头

s = 'This module provides regular expression matching operations similar to those found in Perl'

pat = r'^[emrt]' # 查找以字符e,m,r或t开始的字符串

r = re.findall(pat,s)

print(r)# [],因为字符串的开头是字符`T`,不在emrt匹配范围内,所以返回为空

IN [11]: s2 = 'email for me is guozhennianhua@163.com'

re.findall('^[emrt].*',s2)# 匹配以e,m,r,t开始的字符串,后面是多个任意字符

Out[11]: ['email for me is guozhennianhua@163.com']

 

6 re.I 忽略大小写

s = 'That'

pat = r't' 

r = re.findall(pat,s,re.I)

In [22]: r

Out[22]: ['T', 't']

7 理解compile的作用

如果要做很多次匹配,可以先编译匹配串:

import re

pat = re.compile('\W+') # \W 匹配不是数字和字母的字符

has_special_chars = pat.search('ed#2@edc') if has_special_chars:

    print(f'str contains special characters:{has_special_chars.group(0)}')

###输出结果:

 # str contains special characters:#   

### 再次使用pat正则编译对象 做匹配

again_pattern = pat.findall('guozhennianhua@163.com')if '@' in again_pattern:

    print('possibly it is an email')

 

8 使用()捕获单词,不想带空格

使用()捕获

s = 'This module provides regular expression matching operations similar to those found in Perl'

pat = r'\s([a-zA-Z]+)'  

r = re.findall(pat,s)

print(r) #['module', 'provides', 'regular', 'expression', 'matching', 'operations', 'similar', 'to', 'those', 'found', 'in', 'Perl']

看到提取单词中未包括第一个单词,使用?表示前面字符出现0次或1次,但是此字符还有表示贪心或非贪心匹配含义,使用时要谨慎。

s = 'This module provides regular expression matching operations similar to those found in Perl'

pat = r'\s?([a-zA-Z]+)'  

r = re.findall(pat,s)

print(r) #['This', 'module', 'provides', 'regular', 'expression', 'matching', 'operations', 'similar', 'to', 'those', 'found', 'in', 'Perl']

9 split分割单词

使用以上方法分割单词不是简洁的,仅仅是为了演示。分割单词最简单还是使用split函数。

s = 'This module provides regular expression matching operations similar to those found in Perl'

pat = r'\s+'  

r = re.split(pat,s)

print(r) # ['This', 'module', 'provides', 'regular', 'expression', 'matching', 'operations', 'similar', 'to', 'those', 'found', 'in', 'Perl']

### 上面这句话也可直接使用str自带的split函数:

s.split(' ') #使用空格分隔

### 但是,对于风格符更加复杂的情况,split无能为力,只能使用正则

 

s = 'This,,,   module ; \t   provides|| regular ; '

words = re.split('[,\s;|]+',s)  #这样分隔出来,最后会有一个空字符串

words = [i for i in words if len(i)>0]

10 match从字符串开始位置匹配

注意match,search等的不同:

1. match函数

import re### match

mystr = 'This'

pat = re.compile('hi')

pat.match(mystr) # None

pat.match(mystr,1) # 从位置1处开始匹配

Out[90]: <re.Match object; span=(1, 3), match='hi'>

1. search函数 search是从字符串的任意位置开始匹配

In [91]: mystr = 'This'

    ...: pat = re.compile('hi')

    ...: pat.search(mystr)

Out[91]: <re.Match object; span=(1, 3), match='hi'>

11 替换匹配的子串

sub函数实现对匹配子串的替换

content="hello 12345, hello 456321"    

pat=re.compile(r'\d+') #要替换的部分

m=pat.sub("666",content)

print(m) # hello 666, hello 666

12 贪心捕获

(.*)表示捕获任意多个字符,尽可能多的匹配字符

content='<h>ddedadsad</h><div>graph</div>bb<div>math</div>cc'

pat=re.compile(r"<div>(.*)</div>")  #贪婪模式

m=pat.findall(content)

print(m) #匹配结果为:['graph</div>bb<div>math']

13 非贪心捕获

仅添加一个问号(?),得到结果完全不同,这是非贪心匹配,通过这个例子体会贪心和非贪心的匹配的不同。

content='<h>ddedadsad</h><div>graph</div>bb<div>math</div>cc'

pat=re.compile(r"<div>(.*?)</div>")

m=pat.findall(content)

print(m) # ['graph', 'math']

非贪心捕获,见好就收。

14 常用元字符总结

. 匹配任意字符  

^ 匹配字符串开始位置

$ 匹配字符串中结束的位置

* 前面的原子重复0次、1次、多次

? 前面的原子重复0次或者1次

+ 前面的原子重复1次或多次

{n} 前面的原子出现了 n 次

{n,} 前面的原子至少出现 n 次

{n,m} 前面的原子出现次数介于 n-m 之间

( ) 分组,需要输出的部分

15 常用通用字符总结

\s  匹配空白字符

\w  匹配任意字母/数字/下划线

\W  和小写 w 相反,匹配任意字母/数字/下划线以外的字符

\d  匹配十进制数字

\D  匹配除了十进制数以外的值

[0-9]  匹配一个0-9之间的数字

[a-z]  匹配小写英文字母

[A-Z]  匹配大写英文字母

14 密码安全检查

密码安全要求:1)要求密码为6到20位; 2)密码只包含英文字母和数字

pat = re.compile(r'\w{6,20}') # 这是错误的,因为\w通配符匹配的是字母,数字和下划线,题目要求不能含有下划线# 使用最稳的方法:\da-zA-Z满足`密码只包含英文字母和数字`

pat = re.compile(r'[\da-zA-Z]{6,20}')

选用最保险的fullmatch方法,查看是否整个字符串都匹配:

pat.fullmatch('qaz12') # 返回 None, 长度小于6

pat.fullmatch('qaz12wsxedcrfvtgb67890942234343434') # None 长度大于22

pat.fullmatch('qaz_231') # None 含有下划线

pat.fullmatch('n0passw0Rd')

Out[4]: <re.Match object; span=(0, 10), match='n0passw0Rd'>

15 爬取百度首页标题

import refrom urllib import request

#爬虫爬取百度首页内容

data=request.urlopen("http://www.baidu.com/").read().decode()

#分析网页,确定正则表达式

pat=r'<title>(.*?)</title>'

 

result=re.search(pat,data)

print(result) <re.Match object; span=(1358, 1382), match='<title>百度一下,你就知道</title>'>

 

result.group() # 百度一下,你就知道

16 批量转化为驼峰格式(Camel)

数据库字段名批量转化为驼峰格式

分析过程

# 用到的正则串讲解# \s 指匹配:[ \t\n\r\f\v]# A|B:表示匹配A串或B串# re.sub(pattern, newchar, string): # substitue代替,用newchar字符替代与pattern匹配的字符所有.

# title(): 转化为大写,例子:# 'Hello world'.title() # 'Hello World'

# print(re.sub(r"\s|_|", "", "He llo_worl\td"))

s = re.sub(r"(\s|_|-)+", " ",

           'some_database_field_name').title().replace(" ", "")  #结果:SomeDatabaseFieldName

# 可以看到此时的第一个字符为大写,需要转化为小写

s = s[0].lower()+s[1:]  # 最终结果

整理以上分析得到如下代码:

import redef camel(s):

    s = re.sub(r"(\s|_|-)+", " ", s).title().replace(" ", "")

    return s[0].lower() + s[1:]

# 批量转化def batch_camel(slist):

    return [camel(s) for s in slist]

测试结果:

s = batch_camel(['student_id', 'student\tname', 'student-add'])

print(s)# 结果

['studentId', 'studentName', 'studentAdd']

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值