挑战全网目前最全python例子(附源码),独此一家,经典值得收藏!!!(二):Python之坑,Python字符串和正则

上一篇文章:挑战全网目前最全python例子(附源码),独此一家,经典值得收藏!!!(一):感受Python之美,Python基础

 

二 Python之坑

免费领取Python自动化学习资料  工具,面试宝典面试技巧,加QQ群,785128166,群内还会大佬技术交流

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 = 1
def f():
    i+=1 #NO!
    
def g():
    print(i)
复制代码

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

i = 1
def 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 = 2
def 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
init
del
del
Out[63]: False

复制代码

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

In [64]: id(SE()) == id(SE())
init
del
init
del
Out[64]: True
复制代码

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

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

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

11 充分认识for

In [65]: for i in range(5):
    ...:   print(i)
    ...:   i = 10
0
1
2
3
4
复制代码

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

按照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字符串和正则

(免费领取Python自动化学习资料  工具,面试宝典面试技巧,加QQ群,785128166,群内还会大佬技术交流)

字符串无所不在,字符串的处理也是最常见的操作。本章节将总结和字符串处理相关的一切操作。主要包括基本的字符串操作;高级字符串操作之正则。目前共有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 re
from 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 re
def 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']


作者:程序员聚宝盆
链接:https://juejin.im/post/6844904034613067783
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值