Python(四)裝飾器、迭代器&生成器、re正則表達式、字符串格式化

本章內容:

  • 裝飾器

  • 迭代器 & 生成器

  • re 正則表達式

  • 字符串格式化


裝飾器


 裝飾器是一個很著名的設計模式,經常被用於有切面需求的場景,較爲經典的有插入日誌、性能測試、事務處理等。裝飾器是解決這類問題的絕佳設計,有了裝飾器,我們就可以抽離出大量函數中與函數功能本身無關的雷同代碼並繼續重用。概括的講,裝飾器的作用就是爲已經存在的對象添加額外的功能。

先定義一個基本的裝飾器:

########## 基本裝飾器 ##########
def orter(func):    #定義裝飾器
    def inner():
        print("This is inner before.")
        s = func()    #調用原傳入參數函數執行
        print("This is inner after.")
        return s        #return原函數返回值
    return inner      #將inner函數return給name函數

@orter    #調用裝飾器(將函數name當參數傳入orter裝飾器)
def name():
    print("This is name.")
    return True        #name原函數return True 

ret = name()
print(ret)

輸出結果:
This is inner before.
This is name.
This is inner after.
True


給裝飾器傳參數:

############ 裝飾器傳參數 ###########
def orter(func):
    def inner(a,b):      #接收傳入的2個參數
        print("This is inner before.")
        s = func(a,b)    #接收傳入的原函數2個參數
        print("This is inner after.")
        return s
    return inner

@orter
def name(a,b):    #接收傳入的2個參數,並name整體函數當參數傳入orter裝飾器
    print("This is name.%s,%s"%(a,b))
    return True

ret = name('nick','jenny')    #傳入2個參數
print(ret)

輸出結果:
This is inner before.
This is name.nick,jenny
This is inner after.
True

給裝飾器傳萬能參數:

########## 萬能參數裝飾器 ##########

def orter(func):
    def inner(*args,**kwargs):        #萬能參數接收多個參數
        print("This is inner before.")
        s = func(*args,**kwargs)       #萬能參數接收多個參數
        print("This is inner after.")
        return s
    return inner

@orter
def name(a,b,c,k1='nick'):        #接受傳入的多個參數
    print("This is name.%s,%s"%(a,b))
    return True

ret = name('nick','jenny','car')
print(ret)

輸出結果:
This is inner before.
This is name.nick,jenny
This is inner after.
True


一個函數應用多個裝飾器方法:

########### 一個函數應用多個裝飾器 #########

def orter(func):
    def inner(*args,**kwargs):
        print("This is inner one before.")
        print("This is inner one before angin.")
        s = func(*args,**kwargs)
        print("This is inner one after.")
        print("This is inner one after angin.")
        return s
    return inner

def orter_2(func):
    def inner(*args,**kwargs):
        print("This is inner two before.")
        print("This is inner two before angin.")
        s = func(*args,**kwargs)
        print("This is inner two after.")
        print("This is inner two after angin.")
        return s
    return inner

@orter            #將以下函數整體當參數傳入orter裝飾器  
@orter_2          #將以下函數當參數傳入orter_2裝飾器  
def name(a,b,c,k1='nick'):
    print("This is name.%s and %s."%(a,b))
    return True

ret = name('nick','jenny','car')
print(ret)

輸出結果:
This is inner one before.
This is inner one before angin.
This is inner two before.
This is inner two before angin.
This is name.nick and jenny.
This is inner two after.
This is inner two after angin.
This is inner one after.
This is inner one after angin.
True


932699-20160516224246544-35099591.png

迭代器 & 生成器


1、迭代器

  迭代器只不過是一個實現迭代器協議的容器對象。

特點:

  1. 訪問者不需要關心迭代器內部的結構,僅需通過next()方法不斷去取下一個內容

  2. 不能隨機訪問集合中的某個值 ,只能從頭到尾依次訪問

  3. 訪問到一半時不能往回退

  4. 便於循環比較大的數據集合,節省內存

a = iter([1,2,3,4])
print a
print a.next()
print a.next()
print a.next()
print a.next()
print a.next()


<listiterator object at 0x00000000023E9710>
1
2
3
4
Traceback (most recent call last):
  File "D:/python/untitled4/test.py", line 23, in <module>
    print a.next()
StopIteration


2、生成器

  一個函數調用時返回一個迭代器,那這個函數就叫做生成器(generator);如果函數中包含yield語法,那這個函數就會變成生成器。

def xran():
    print ("one")
    yield 1
    print "two"
    yield 2
    print "sr"
    yield 3

ret = xran()
#print ret  #<generator object xran at 0x00000000022D8AB0>


result = ret.next()
print result

result = ret.next()
print result

result = ret.next()
print result


# ret.next()  #循環完畢拋出異常
# ret.close() #關閉生成器

one
1
two
2
sr
3


生成器的表達式:

a = [1,2,3]
b = [i+3 for i in a]
print b
print type(b)


ib = (i+3 for i in a)
print ib
print ib.next()
print ib.next()
print ib.next()


[4, 5, 6]
<type 'list'>
<generator object <genexpr> at 0x00000000023E8A20>
4
5
6


正則表達式:


正則表達式是用來匹配字符串非常強大的工具,在其他編程語言中同樣有正則表達式的概念。就其本質而言,正則表達式(或 RE)是一種小型的、高度專業化的編程語言,(在Python中)它內嵌在Python中,並通過 re 模塊實現。正則表達式模式被編譯成一系列的字節碼,然後由用 C 編寫的匹配引擎執行。

#導入 re 模塊
import re
 
s = 'nick jenny nice'
 
# 匹配方式(一)
b = re.match(r'nick',s)
q = b.group()
print(q)
 
# 匹配方式(二)
# 生成Pattern對象實例,r表示匹配源字符串
a = re.compile(r'nick')
print(type(a))               #<class '_sre.SRE_Pattern'>
 
b = a.match(s)
print(b)                     #<_sre.SRE_Match object; span=(0, 4), match='nick'>
 
q = b.group()
print(q)
 
 
#被匹配的字符串放在string中
print(b.string)              #nick jenny nice
#要匹配的字符串放在re中
print(b.re)                  #re.compile('nick')

兩種匹配方式區別在於:第一種簡寫是每次匹配的時候都要進行一次匹配公式的編譯,第二種方式是提前對要匹配的格式進行了編譯(對匹配公式進行解析),這樣再去匹配的時候就不用在編譯匹配的格式。

匹配規則:

 .
  "." 匹配任意字符(除了\n)
  \
  "\" 轉義字符
  [...]
  "[...]" 匹配字符集
<br># "." 匹配任意字符(除了\n)
a = re.match(r".","95nick")
b = a.group()
print(b)
 
# [...] 匹配字符集
a = re.match(r"[a-zA-Z0-9]","123Nick")
b = a.group()
print(b)
 \d   
  匹配任何十進制數;它相當於類 [0-9]
  \D
  匹配任何非數字字符;它相當於類 [^0-9]
  \s
  匹配任何空白字符;它相當於類 [\t\n\r\f\v]
  \S
  匹配任何非空白字符;它相當於類 [^\t\n\r\f\v]
  \w
  匹配任何字母數字字符;它相當於類 [a-zA-Z0-9]
  \W  
  匹配任何非字母數字字符;它相當於類 [^a-zA-Z0-9]
# \d \D 匹配數字/非數字
a = re.match(r"\D","nick")
b = a.group()
print(b)
 
# \s \S 匹配空白/非空白字符
a = re.match(r"\s"," ")
b = a.group()
print(b)
 
# \w \W 匹配單詞字符[a-zA-Z0-9]/非單詞字符
a = re.match(r"\w","123Nick")
b = a.group()
print(b)
a = re.match(r"\W","+-*/")
b = a.group()
print(b)
 *
   "*" 匹配前一個字符0次或者無限次
  +
   "+" 匹配前一個字符1次或者無限次
  ?
    "?" 匹配一個字符0次或者1次

   {m} {m,n}

    {m} {m,n} 匹配前一個字符m次或者m到n次

   *? +? ??

    *? +? ?? 匹配模式變爲非貪婪(儘可能少匹配字符串)
# "*" 匹配前一個字符0次或者無限次
a = re.match(r"[A-Z][a-z]*","Aaaaaa123")    #可以只匹配A,123不會匹配上
b = a.group()
print(b)
 
# “+” 匹配前一個字符1次或者無限次
a = re.match(r"[_a-zA-Z]+","nick")
b = a.group()
print(b)
 
# “?” 匹配一個字符0次或者1次
a = re.match(r"[0-8]?[0-9]","95")   #(0-8)沒有匹配上9
b = a.group()
print(b)
 
# {m} {m,n} 匹配前一個字符m次或者m到n次
a = re.match(r"[\w]{6,10}@qq.com","[email protected]")
b = a.group()
print(b)
 
# *? +? ?? 匹配模式變爲非貪婪(儘可能少匹配字符串)
a = re.match(r"[0-9][a-z]*?","9nick")
b = a.group()
print(b)
a = re.match(r"[0-9][a-z]+?","9nick")
b = a.group()
print(b)
  ^  
   "^" 匹配字符串開頭,多行模式中匹配每一行的開頭
   $
   "$" 匹配字符串結尾,多行模式中匹配每一行的末尾
   \A
   \A 僅匹配字符串開頭
    \Z

   \Z 僅匹配字符串結尾

    \b

   \b 匹配一個單詞邊界,也就是指單詞和空格間的位置
# "^" 匹配字符串開頭,多行模式中匹配每一行的開頭。
li = "nick\nnjenny\nsuo"
a = re.search("^s.*",li,re.M)
b = a.group()
print(b)
 
# "$" 匹配字符串結尾,多行模式中匹配每一行的末尾。
li = "nick\njenny\nnick"
a = re.search(".*y$",li,re.M)
b = a.group()
print(b)
 
# \A 僅匹配字符串開頭
li = "nickjennyk"
a = re.findall(r"\Anick",li)
print(a)
 
# \Z 僅匹配字符串結尾
li = "nickjennyk"
a = re.findall(r"nick\Z",li)
print(a)
 
# \b 匹配一個單詞邊界,也就是指單詞和空格間的位置
a = re.search(r"\bnick\b","jenny nick car")
b = a.group()
print(b)
 |
  "|" 匹配左右任意一個表達式
  ab
  (ab) 括號中表達式作爲一個分組
  \<number>
  \<number> 引用編號爲num的分組匹配到的字符串
  (?P<key>vlaue)
  (?P<key>vlaue) 匹配到一個字典,去vlaue也可做別名
  (?P=name)
  (?P=name) 引用別名爲name的分組匹配字符串
# "|" 匹配左右任意一個表達式
a = re.match(r"nick|jenny","jenny")
b = a.group()
print(b)
 
# (ab) 括號中表達式作爲一個分組
a = re.match(r"[\w]{6,10}@(qq|163).com","[email protected]")
b = a.group()
print(b)
 
# \<number> 引用編號爲num的分組匹配到的字符串
a = re.match(r"<([\w]+>)[\w]+</\1","<book>nick</book>")
b = a.group()
print(b)
 
# (?P<key>vlace) 匹配輸出字典
li = 'nick jenny nnnk'
a = re.match("(?P<k1>n)(?P<k2>\w+).*(?P<k3>n\w+)",li)
print(a.groupdict())
輸出結果:
{'k2': 'ick', 'k1': 'n', 'k3': 'nk'}
 
# (?P<name>) 分組起一個別名
# (?P=name) 引用別名爲name的分組匹配字符串
a = re.match(r"<(?P<jenny>[\w]+>)[\w]+</(?P=jenny)","<book>nick</book>")
b = a.group()
print(b)


 模塊方法介紹

 match

   從頭匹配

  search
  匹配整個字符串,直到找到一個匹配
  findall
  找到匹配,返回所有匹配部分的列表

   finditer

  返回一個迭代器

  sub

  將字符串中匹配正則表達式的部分替換爲其他值

  split

  根據匹配分割字符串,返回分割字符串組成的列表
######## 模塊方法介紹 #########
# match 從頭匹配
 
# search 匹配整個字符串,直到找到一個匹配
 
# findall 找到匹配,返回所有匹配部分的列表
# findall 加括號
li = 'nick jenny nick car girl'
 
r = re.findall('n\w+',li)
print(r)
#輸出結果:['nick', 'nny', 'nick']
r = re.findall('(n\w+)',li)
print(r)
#輸出結果:['nick', 'nny', 'nick']
r = re.findall('n(\w+)',li)
print(r)
#輸出結果:['ick', 'ny', 'ick']
r = re.findall('(n)(\w+)(k)',li)
print(r)
#輸出結果:[('n', 'ic', 'k'), ('n', 'ic', 'k')]
r = re.findall('(n)((\w+)(c))(k)',li)
print(r)
#輸出結果:[('n', 'ic', 'i', 'c', 'k'), ('n', 'ic', 'i', 'c', 'k')]
 
 
# finditer 返回一個迭代器,和findall一樣
li = 'nick jenny nnnk'
a = re.finditer(r'n\w+',li)
for i in a:
    print(i.group())
 
# sub 將字符串中匹配正則表達式的部分替換爲其他值
li = 'This is 95'
a = re.sub(r"\d+","100",li)
print(a)
 
li = "nick njenny ncar ngirl"
a = re.compile(r"\bn")
b = a.sub('cool',li,3)      #後邊參數替換幾次
print(b)
 
#輸出結果:
#coolick cooljenny coolcar ngirl
 
# split 根據匹配分割字符串,返回分割字符串組成的列表
li = 'nick,suo jenny:nice car'
a = re.split(r":| |,",li)   # 或|,分割 :, 空白符
print(a)
 
li = 'nick1jenny2car3girl5'
a = re.compile(r"\d")
b = a.split(li)
print(b)
 
#輸出結果:
#['nick', 'jenny', 'car', 'girl', '']   #注意後邊空元素
 group()
  返回被 RE 匹配的字符串
  groups()
 
  返回一個包含正則表達式中所有小組字符串的元組,從 1 到所含的小組號
  groupdict()
 
  返回(?P<key>vlace)定義的字典
  start()
  返回匹配開始的位置
  end()
  返回匹配結束的位置
  span()
  返回一個元組包含匹配 (開始,結束) 的索引位置
li = 'nick jenny nnnk'
 
a = re.match("n\w+",li)
print(a.group())
 
a = re.match("(n)(\w+)",li)
print(a.groups())
 
a = re.match("(?P<k1>n)(?P<k2>\w+).*(?P<k3>n\w+)",li)
print(a.groupdict())
 
-----------------------------------------------
import re
a = "123abc456"
 re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0)   #123abc456,返回整體
 re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1)   #123
 re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2)   #abc
 re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3)   #456
 
 group(1) 列出第一個括號匹配部分,group(2) 列出第二個括號匹配部分,group(3)列出第三個括號匹配部分。
 
-----------------------------------------------
 re.I
  使匹配對大小寫不敏感
  re.L
  做本地化識別(locale-aware)匹配
  re.M
  多行匹配,影響 ^ 和 $
  re.S  
  使 . 匹配包括換行在內的所有字符
  re.U
  根據Unicode字符集解析字符。這個標誌影響 \w, \W, \b, \B.
  re.X
 
  註釋,會影響空格(無效了)
#re.I   使匹配對大小寫不敏感
a = re.search(r"nick","NIck",re.I)
print(a.group())
 
#re.L   做本地化識別(locale-aware)匹配
#re.U   根據Unicode字符集解析字符。這個標誌影響 \w, \W, \b, \B.
 
#re.S:.將會匹配換行符,默認.逗號不會匹配換行符
a = re.findall(r".","nick\njenny",re.S)
print(a)
輸出結果:
['n', 'i', 'c', 'k', '\n', 'j', 'e', 'n', 'n', 'y']
 
#re.M:^$標誌將會匹配每一行,默認^只會匹配符合正則的第一行;默認$只會匹配符合正則的末行
n = """12 drummers drumming,
11 pipers piping, 10 lords a-leaping"""
 
p = re.compile("^\d+")
p_multi = re.compile("^\d+",re.M)
print(re.findall(p,n))
print(re.findall(p_multi,n))


常見正則列子:

匹配手機號:

# 匹配手機號
phone_num = '13001000000'
a = re.compile(r"^1[\d+]{10}")
b = a.match(phone_num)
print(b.group())

匹配IPv4:

# 匹配IP地址
ip = '192.168.1.1'
a = re.compile(r"(((1?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))\.){3}((1?[0-9]?[0-9])|(2[0-4][0-9])|(25[0-5]))$")
b = a.search(ip)
print(b)

匹配email:

# 匹配 email
email = '[email protected]'
a = re.compile(r"(.*){0,26}@(\w+){0,20}.(\w+){0,8}")
b = a.search(email)
print(b.group())


字符串格式化:

1、百分號方式

%[(name)][flags][width].[precision]typecode

  • (name)   可選,用於選擇指定的key

  • flags    可選,可供選擇的值有:width         可選,佔有寬度

    • +     右對齊;正數前加正好,負數前加負號;

    • -     左對齊;正數前無符號,負數前加負號;

    • 空格  右對齊;正數前加空格,負數前加負號;

    • 0     右對齊;正數前無符號,負數前加負號;用0填充空白處



  • .precision  可選,小數點後保留的位數

  • typecode    必選

    • s,獲取傳入對象的__str__方法的返回值,並將其格式化到指定位置

    • r,獲取傳入對象的__repr__方法的返回值,並將其格式化到指定位置

    • c,整數:將數字轉換成其unicode對應的值,10進制範圍爲 0 <= i <= 1114111(py27則只支持0-255);字符:將字符添加到指定位置

    • o,將整數轉換成 八  進製表示,並將其格式化到指定位置

    • x,將整數轉換成十六進制表示,並將其格式化到指定位置

    • d,將整數、浮點數轉換成 十 進製表示,並將其格式化到指定位置

    • e,將整數、浮點數轉換成科學計數法,並將其格式化到指定位置(小寫e)

    • E,將整數、浮點數轉換成科學計數法,並將其格式化到指定位置(大寫E)

    • f, 將整數、浮點數轉換成浮點數表示,並將其格式化到指定位置(默認保留小數點後6位)

    • F,同上

    • g,自動調整將整數、浮點數轉換成 浮點型或科學計數法表示(超過6位數用科學計數法),並將其格式化到指定位置(如果是科學計數則是e;)

    • G,自動調整將整數、浮點數轉換成 浮點型或科學計數法表示(超過6位數用科學計數法),並將其格式化到指定位置(如果是科學計數則是E;)

    • %,當字符串中存在格式化標誌時,需要用 %%表示一個百分號


常用格式化:
 
tpl = "i am %s" % "nick"
  
tpl = "i am %s age %d" % ("nick", 18)
  
tpl = "i am %(name)s age %(age)d" % {"name": "nick", "age": 18}
  
tpl = "percent %.2f" % 99.97623
  
tpl = "i am %(pp).2f" % {"pp": 123.425556, }
  
tpl = "i am %.2f %%" % {"pp": 123.425556, }

2、Format方式

[[fill]align][sign][#][0][width][,][.precision][type]


  • fill    【可選】空白處填充的字符

  • align   【可選】對齊方式(需配合width使用)

    • <,內容左對齊

    • >,內容右對齊(默認)

    • =,內容右對齊,將符號放置在填充字符的左側,且只對數字類型有效。 即使:符號+填充物+數字

    • ^,內容居中

  • sign    【可選】有無符號數字

    • +,正號加正,負號加負;

    •  -,正號不變,負號加負;

    • 空格 ,正號空格,負號加負;


  • #       【可選】對於二進制、八進制、十六進制,如果加上#,會顯示 0b/0o/0x,否則不顯示

  • ,      【可選】爲數字添加分隔符,如:1,000,000

  • width   【可選】格式化位所佔寬度

  • .precision 【可選】小數位保留精度

  • type    【可選】格式化類型

    • 傳入” 字符串類型 “的參數

    • 傳入“ 整數類型 ”的參數

    • 傳入“ 浮點型或小數類型 ”的參數

    • s,格式化字符串類型數據

    • 空白,未指定類型,則默認是None,同s

    • b,將10進制整數自動轉換成2進製表示然後格式化

    • c,將10進制整數自動轉換爲其對應的unicode字符

    • d,十進制整數

    • o,將10進制整數自動轉換成8進製表示然後格式化;

    • x,將10進制整數自動轉換成16進製表示然後格式化(小寫x)

    • X,將10進制整數自動轉換成16進製表示然後格式化(大寫X)

    • e, 轉換爲科學計數法(小寫e)表示,然後格式化;

    • E, 轉換爲科學計數法(大寫E)表示,然後格式化;

    • f , 轉換爲浮點型(默認小數點後保留6位)表示,然後格式化;

    • F, 轉換爲浮點型(默認小數點後保留6位)表示,然後格式化;

    • g, 自動在e和f中切換

    • G, 自動在E和F中切換

    • %,顯示百分比(默認顯示小數點後6位)


常用格式化:
 
tpl = "i am {}, age {}, {}".format("nick", 18, 'jenny')
   
tpl = "i am {}, age {}, {}".format(*["nick", 18, 'jenny'])
   
tpl = "i am {0}, age {1}, really {0}".format("nick", 18)
   
tpl = "i am {0}, age {1}, really {0}".format(*["nick", 18])
   
tpl = "i am {name}, age {age}, really {name}".format(name="nick", age=18)
   
tpl = "i am {name}, age {age}, really {name}".format(**{"name": "nick", "age": 18})
   
tpl = "i am {0[0]}, age {0[1]}, really {0[2]}".format([1, 2, 3], [11, 22, 33])
   
tpl = "i am {:s}, age {:d}, money {:f}".format("nick", 18, 88888.1)
 
tpl = "i am {:s}, age {:d}, money {:0.2f}".format("nick", 18, 88888.111111111111)
   
tpl = "i am {:s}, age {:d}".format(*["nick", 18])
   
tpl = "i am {name:s}, age {age:d}".format(name="nick", age=18)
   
tpl = "i am {name:s}, age {age:d}".format(**{"name": "nick", "age": 18})
  
tpl = "numbers: {:b},{:o},{:d},{:x},{:X}, {:%}".format(15, 15, 15, 15, 15, 15.87623, 2)
  
tpl = "numbers: {0:b},{0:o},{0:d},{0:x},{0:X}, {0:%}".format(15)
  
tpl = "numbers: {num:b},{num:o},{num:d},{num:x},{num:X}, {num:%}".format(num=15)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章