1. 程式人生 > 資料庫 >django框架之聚合、分組查詢和原生SQL等相關內容-69

django框架之聚合、分組查詢和原生SQL等相關內容-69

<div id="page_begin_html">
    <script>loadPageBeginHtml();</script>
</div>
<!--done-->

太白金星

</div><!--end: blogTitle 部落格的標題和副標題 -->
<div id="navigator">
	<div class="blogStats">
		<span id="stats_post_count">隨筆 - 6&nbsp; </span>

文章 - 121
評論 - 239

	</div><!--end: blogStats -->
</div><!--end: navigator 部落格導航欄 -->

python常用模組
        </h1>
        <div class="clear"></div>
        <div class="postBody">

一,什麼是模組?

常見的場景:一個模組就是一個包含了python定義和宣告的檔案,檔名就是模組名字加上.py的字尾。

但其實import載入的模組分為四個通用類別: 

  1 使用python編寫的程式碼(.py檔案)

  2 已被編譯為共享庫或DLL的C或C++擴充套件

  3 包好一組模組的包

  4 使用C編寫並連結到python直譯器的內建模組

為何要使用模組?

如果你退出python直譯器然後重新進入,那麼你之前定義的函式或者變數都將丟失,因此我們通常將程式寫到檔案中以便永久儲存下來,需要時就通過python test.py方式去執行,此時test.py被稱為指令碼script。

隨著程式的發展,功能越來越多,為了方便管理,我們通常將程式分成一個個的檔案,這樣做程式的結構更清晰,方便管理。這時我們不僅僅可以把這些檔案當做指令碼去執行,還可以把他們當做模組來匯入到其他的模組中,實現了功能的重複利用,

二,序列化模組。

什麼叫序列化——將原本的字典、列表等內容轉換成一個字串的過程就叫做序列化

比如,我們在python程式碼中計算的一個數據需要給另外一段程式使用,那我們怎麼給?
現在我們能想到的方法就是存在檔案裡,然後另一個python程式再從檔案裡讀出來。
但是我們都知道,對於檔案來說是沒有字典這個概念的,所以我們只能將資料轉換成字典放到檔案中。
你一定會問,將字典轉換成一個字串很簡單,就是str(dic)就可以辦到了,為什麼我們還要學習序列化模組呢?
沒錯序列化的過程就是從dic 變成str(dic)的過程。現在你可以通過str(dic),將一個名為dic的字典轉換成一個字串,
但是你要怎麼把一個字串轉換成字典呢?
聰明的你肯定想到了eval(),如果我們將一個字串型別的字典str_dic傳給eval,就會得到一個返回的字典型別了。
eval()函式十分強大,但是eval是做什麼的?e官方demo解釋為:將字串str當成有效的表示式來求值並返回計算結果。
BUT!強大的函式有代價。安全性是其最大的缺點。
想象一下,如果我們從檔案中讀出的不是一個數據結構,而是一句"刪除檔案"類似的破壞性語句,那麼後果實在不堪設設想。
而使用eval就要擔這個風險。
所以,我們並不推薦用eval方法來進行反序列化操作(將str轉換成python中的資料結構)
為什麼要有序列化模組

序列化的目的

1、以某種儲存形式使自定義物件持久化; 2、將物件從一個地方傳遞到另一個地方。 3、使程式更具維護性。
| Python      |     JSON      |

| dict | object |

| list, tuple | array |

| str | string |

| int, float | number |

| True | true |

| False | false |

| None | null |

Python可序列化的資料型別

2.1 json模組

Json模組提供了四個功能:dumps、dump、loads、load

import json
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = json.dumps(dic)  #序列化:將一個字典轉換成一個字串
print(type(str_dic),str_dic)  #<class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"}
#注意,json轉換完的字串型別的字典中的字串是由""表示的

dic2 = json.loads(str_dic)  #反序列化:將一個字串格式的字典轉換成一個字典
#注意,要用json的loads功能處理的字串型別的字典中的字串必須由""表示
print(type(dic2),dic2)  #<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}

list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic
= json.dumps(list_dic) #也可以處理巢狀的資料型別
print(type(str_dic),str_dic) #<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
list_dic2 = json.loads(str_dic)
print(type(list_dic2),list_dic2) #<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]

loads和dumps

dumps loads
import json
f = open('json_file','w')
dic = {'k1':'v1','k2':'v2','k3':'v3'}
json.dump(dic,f)  #dump方法接收一個檔案控制代碼,直接將字典轉換成json字串寫入檔案
f.close()

f = open('json_file')
dic2
= json.load(f) #load方法接收一個檔案控制代碼,直接將檔案中的json字串轉換成資料結構返回
f.close()
print(type(dic2),dic2)

dump load
Serialize obj to a JSON formatted str.(字串表示的json物件) 
Skipkeys:預設值是False,如果dict的keys內的資料不是python的基本型別(str,unicode,int,long,float,bool,None),設定為False時,就會報TypeError的錯誤。此時設定成True,則會跳過這類key 
ensure_ascii:,當它為True的時候,所有非ASCII碼字元顯示為\uXXXX序列,只需在dump時將ensure_ascii設定為False即可,此時存入json的中文即可正常顯示。) 
If check_circular is false, then the circular reference check for container types will be skipped and a circular reference will result in an OverflowError (or worse). 
If allow_nan is false, then it will be a ValueError to serialize out of range float values (nan, inf, -inf) in strict compliance of the JSON specification, instead of using the JavaScript equivalents (NaN, Infinity, -Infinity). 
indent:應該是一個非負的整型,如果是0就是頂格分行顯示,如果為空就是一行最緊湊顯示,否則會換行且按照indent的數值顯示前面的空白分行顯示,這樣打印出來的json資料也叫pretty-printed json 
separators:分隔符,實際上是(item_separator, dict_separator)的一個元組,預設的就是(‘,’,’:’);這表示dictionary內keys之間用“,”隔開,而KEY和value之間用“:”隔開。 
default(obj) is a function that should return a serializable version of obj or raise TypeError. The default simply raises TypeError. 
sort_keys:將資料根據keys的值進行排序。 
To use a custom JSONEncoder subclass (e.g. one that overrides the .default() method to serialize additional types), specify it with the cls kwarg; otherwise JSONEncoder is used.
其他引數說明
import json
data = {'username':['李華','二愣子'],'sex':'male','age':16}
json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(',',':'),ensure_ascii=False)
print(json_dic2)
json的格式化輸出

2.2 pickle模組

用於序列化的兩個模組

  • json,用於字串 和 python資料型別間進行轉換
  • pickle,用於python特有的型別 和 python的資料型別間進行轉換

pickle模組提供了四個功能:dumps、dump(序列化,存)、loads(反序列化,讀)、load (不僅可以序列化字典,列表...可以把python中任意的資料型別序列化

import pickle
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = pickle.dumps(dic)
print(str_dic)  #一串二進位制內容

dic2 = pickle.loads(str_dic)
print(dic2)    #字典

import time
struct_time
= time.localtime(1000000000)
print(struct_time)
f
= open('pickle_file','wb')
pickle.dump(struct_time,f)
f.close()

f = open('pickle_file','rb')
struct_time2
= pickle.load(f)
print(struct_time2.tm_year)

pickle

這時候機智的你又要說了,既然pickle如此強大,為什麼還要學json呢?
這裡我們要說明一下,json是一種所有的語言都可以識別的資料結構。
如果我們將一個字典或者序列化成了一個json存在檔案裡,那麼java程式碼或者js程式碼也可以拿來用。
但是如果我們用pickle進行序列化,其他語言就不能讀懂這是什麼了~
所以,如果你序列化的內容是列表或者字典,我們非常推薦你使用json模組
但如果出於某種原因你不得不序列化其他的資料型別,而未來你還會用python對這個資料進行反序列化的話,那麼就可以使用pickle

2.3shelve模組

shelve也是python提供給我們的序列化工具,比pickle用起來更簡單一些。
shelve只提供給我們一個open方法,是用key來訪問的,使用起來和字典類似。

import shelve
f = shelve.open('shelve_file')
f['key'] = {'int':10, 'float':9.5, 'string':'Sample data'}  #直接對檔案控制代碼操作,就可以存入資料
f.close()

import shelve
f1
= shelve.open('shelve_file')
existing
= f1['key'] #取出資料的時候也只需要直接用key獲取即可,但是如果key不存在會報錯
f1.close()
print(existing)

View Code

這個模組有個限制,它不支援多個應用同一時間往同一個DB進行寫操作。所以當我們知道我們的應用如果只進行讀操作,我們可以讓shelve通過只讀方式開啟DB

import shelve
f = shelve.open('shelve_file', flag='r')
existing = f['key']
f.close()
print(existing)
shelve只讀

由於shelve在預設情況下是不會記錄待持久化物件的任何修改的,所以我們在shelve.open()時候需要修改預設引數,否則物件的修改不會儲存。

import shelve
f1 = shelve.open('shelve_file')
print(f1['key'])
f1['key']['new_value'] = 'this was not here before'
f1.close()

f2 = shelve.open('shelve_file', writeback=True)
print(f2['key'])
f2[
'key']['new_value'] = 'this was not here before'
f2.close()

設定writeback

writeback方式有優點也有缺點。優點是減少了我們出錯的概率,並且讓物件的持久化對使用者更加的透明瞭;但這種方式並不是所有的情況下都需要,首先,使用writeback以後,shelf在open()的時候會增加額外的記憶體消耗,並且當DB在close()的時候會將快取中的每一個物件都寫入到DB,這也會帶來額外的等待時間。因為shelve沒有辦法知道快取中哪些物件修改了,哪些物件沒有修改,因此所有的物件都會被寫入。

三,hashlib模組

演算法介紹

Python的hashlib提供了常見的摘要演算法,如MD5,SHA1等等。

什麼是摘要演算法呢?摘要演算法又稱雜湊演算法、雜湊演算法。它通過一個函式,把任意長度的資料轉換為一個長度固定的資料串(通常用16進位制的字串表示)。

摘要演算法就是通過摘要函式f()對任意長度的資料data計算出固定長度的摘要digest,目的是為了發現原始資料是否被人篡改過。

摘要演算法之所以能指出資料是否被篡改過,就是因為摘要函式是一個單向函式,計算f(data)很容易,但通過digest反推data卻非常困難。而且,對原始資料做一個bit的修改,都會導致計算出的摘要完全不同。

我們以常見的摘要演算法MD5為例,計算出一個字串的MD5值:

import hashlib

md5 = hashlib.md5()
md5.update('how to use md5 in python hashlib?')
print md5.hexdigest()

計算結果如下:
d26a53750bc40b38b65a520292f69306

如果資料量很大,可以分塊多次呼叫update(),最後計算的結果是一樣的:

md5 = hashlib.md5()
md5.update('how to use md5 in ')
md5.update('python hashlib?')
print md5.hexdigest()

MD5是最常見的摘要演算法,速度很快,生成結果是固定的128 bit位元組,通常用一個32位的16進位制字串表示。另一種常見的摘要演算法是SHA1,呼叫SHA1和呼叫MD5完全類似:

import hashlib

sha1 = hashlib.sha1()
sha1.update('how to use sha1 in ')
sha1.update('python hashlib?')
print sha1.hexdigest()

SHA1的結果是160 bit位元組,通常用一個40位的16進位制字串表示。比SHA1更安全的演算法是SHA256和SHA512,不過越安全的演算法越慢,而且摘要長度更長。

摘要演算法應用

任何允許使用者登入的網站都會儲存使用者登入的使用者名稱和口令。如何儲存使用者名稱和口令呢?方法是存到資料庫表中:

name    | password
--------+----------
michael | 123456
bob     | abc999
alice   | alice2008

如果以明文儲存使用者口令,如果資料庫洩露,所有使用者的口令就落入黑客的手裡。此外,網站運維人員是可以訪問資料庫的,也就是能獲取到所有使用者的口令。正確的儲存口令的方式是不儲存使用者的明文口令,而是儲存使用者口令的摘要,比如MD5:

username | password
---------+---------------------------------
michael  | e10adc3949ba59abbe56e057f20f883e
bob      | 878ef96e86145580c38c87f0410ad153
alice    | 99b1c2188db85afee403b1536010c2c9

考慮這麼個情況,很多使用者喜歡用123456,888888,password這些簡單的口令,於是,黑客可以事先計算出這些常用口令的MD5值,得到一個反推表:

'e10adc3949ba59abbe56e057f20f883e': '123456'
'21218cca77804d2ba1922c33e0151105': '888888'
'5f4dcc3b5aa765d61d8327deb882cf99': 'password'

這樣,無需破解,只需要對比資料庫的MD5,黑客就獲得了使用常用口令的使用者賬號。

對於使用者來講,當然不要使用過於簡單的口令。但是,我們能否在程式設計上對簡單口令加強保護呢?

由於常用口令的MD5值很容易被計算出來,所以,要確保儲存的使用者口令不是那些已經被計算出來的常用口令的MD5,這一方法通過對原始口令加一個複雜字串來實現,俗稱“加鹽”:

hashlib.md5("salt".encode("utf8"))

經過Salt處理的MD5口令,只要Salt不被黑客知道,即使使用者輸入簡單口令,也很難通過MD5反推明文口令。

但是如果有兩個使用者都使用了相同的簡單口令比如123456,在資料庫中,將儲存兩條相同的MD5值,這說明這兩個使用者的口令是一樣的。有沒有辦法讓使用相同口令的使用者儲存不同的MD5呢?

如果假定使用者無法修改登入名,就可以通過把登入名作為Salt的一部分來計算MD5,從而實現相同口令的使用者也儲存不同的MD5。

摘要演算法在很多地方都有廣泛的應用。要注意摘要演算法不是加密演算法,不能用於加密(因為無法通過摘要反推明文),只能用於防篡改,但是它的單向計算特性決定了可以在不儲存明文口令的情況下驗證使用者口令。

#=========知識儲備==========
#進度條的效果
[#             ]
[##            ]
[###           ]
[####          ]

#指定寬度
print('[%-15s]' %'#')
print('[%-15s]' %'##')
print('[%-15s]' %'###')
print('[%-15s]' %'####')

#列印%
print('%s%%' %(100)) #第二個%號代表取消第一個%的特殊意義

#可傳參來控制寬度
print('[%%-%ds]' %50) #[%-50s]
print(('[%%-%ds]' %50) %'#')
print(('[%%-%ds]' %50) %'##')
print(('[%%-%ds]' %50) %'###')

#=實現列印進度條函式==
import sys
import time

def progress(percent,width=50):
if percent >= 1:
percent
=1
show_str
= ('%%-%ds' % width) % (int(widthpercent)'|')
print('\r%s %d%%' %(show_str, int(100*percent)), end='')

#=應用==
data_size=1025
recv_size
=0
while recv_size < data_size:
time.sleep(
0.1) #模擬資料的傳輸延遲
recv_size+=1024 #每次收1024

percent
=recv_size/data_size #接收的比例
progress(percent,width=70) #進度條的寬度70

列印進度條

四,configparser模組

該模組適用於配置檔案的格式與windows ini檔案類似,可以包含一個或多個節(section),每個節可以有多個引數(鍵=值)。

"""
Django settings for webwx project.

Generated by 'django-admin startproject' using Django 1.10.3.

For more information on this file, see
https://docs.djangoproject.com/en/1.10/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.10/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(file)))

# Quick-start development settings - unsuitable for production

See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'mpnn-s-&amp;+ckg_)gl4sp@8=89us&@*^r1c_81#x-5+$)rf8=3'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []

# Application definition

INSTALLED_APPS
= [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'web',
]

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'webwx.urls'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]

WSGI_APPLICATION = 'webwx.wsgi.application'

# Database

https://docs.djangoproject.com/en/1.10/ref/settings/#databases

DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }

# Password validation

https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ]

# Internationalization

https://docs.djangoproject.com/en/1.10/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# Static files (CSS, JavaScript, Images)

https://docs.djangoproject.com/en/1.10/howto/static-files/

STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR,'static'), )
Django的配置檔案舉例

建立檔案

來看一個好多軟體的常見文件格式如下:

[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes

[bitbucket.org]
User = hg

[topsecret.server.com]
Port = 50022
ForwardX11 = no

如果想用python生成一個這樣的文件怎麼做呢?

import configparser

config = configparser.ConfigParser()

config["DEFAULT"] = {'ServerAliveInterval': '45',
'Compression': 'yes',
'CompressionLevel': '9',
'ForwardX11':'yes'
}

config['bitbucket.org'] = {'User':'hg'}

config['topsecret.server.com'] = {'Host Port':'50022','ForwardX11':'no'}

with open('example.ini', 'w') as configfile:

config.write(configfile)

查詢檔案

import configparser

config = configparser.ConfigParser()

---------------------------查詢檔案內容,基於字典的形式

print(config.sections()) # []

config.read('example.ini')

print(config.sections()) # ['bitbucket.org', 'topsecret.server.com']

print('bytebong.com' in config) # False
print('bitbucket.org' in config) # True

print(config['bitbucket.org']["user"]) # hg

print(config['DEFAULT']['Compression']) #yes

print(config['topsecret.server.com']['ForwardX11']) #no

print(config['bitbucket.org']) #<Section: bitbucket.org>

for key in config['bitbucket.org']: # 注意,有default會預設default的鍵
print(key)

print(config.options('bitbucket.org')) # 同for迴圈,找到'bitbucket.org'下所有鍵

print(config.items('bitbucket.org')) #找到'bitbucket.org'下所有鍵值對

print(config.get('bitbucket.org','compression')) # yes get方法Section下的key對應的value

增刪改操作

import configparser

config = configparser.ConfigParser()

config.read('example.ini')

config.add_section('yuan')

config.remove_section('bitbucket.org')
config.remove_option('topsecret.server.com',"forwardx11")

config.set('topsecret.server.com','k1','11111')
config.set('yuan','k2','22222')

config.write(open('new2.ini', "w"))

五,logging模組

函式式簡單配置

import logging  
logging.debug('debug message')  
logging.info('info message')  
logging.warning('warning message')  
logging.error('error message')  
logging.critical('critical message') 

預設情況下Python的logging模組將日誌列印到了標準輸出中,且只顯示了大於等於WARNING級別的日誌,這說明預設的日誌級別設定為WARNING(日誌級別等級CRITICAL > ERROR > WARNING > INFO > DEBUG),預設的日誌格式為日誌級別:Logger名稱:使用者輸出訊息。

靈活配置日誌級別,日誌格式,輸出位置:

import logging  
logging.basicConfig(level=logging.DEBUG,  
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',  
                    datefmt='%a, %d %b %Y %H:%M:%S',  
                    filename='/tmp/test.log',  
                    filemode='w')  

logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

logging.basicConfig()函式中可通過具體引數來更改logging模組預設行為,可用引數有:

filename:用指定的檔名建立FiledHandler,這樣日誌會被儲存在指定的檔案中。
filemode:檔案開啟方式,在指定了filename時使用這個引數,預設值為“a”還可指定為“w”。
format:指定handler使用的日誌顯示格式。
datefmt:指定日期時間格式。
level:設定rootlogger(後邊會講解具體概念)的日誌級別
stream:用指定的stream建立StreamHandler。可以指定輸出到sys.stderr,sys.stdout或者檔案(f=open(‘test.log’,’w’)),預設為sys.stderr。若同時列出了filename和stream兩個引數,則stream引數會被忽略。

format引數中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 數字形式的日誌級別
%(levelname)s 文字形式的日誌級別
%(pathname)s 呼叫日誌輸出函式的模組的完整路徑名,可能沒有
%(filename)s 呼叫日誌輸出函式的模組的檔名
%(module)s 呼叫日誌輸出函式的模組名
%(funcName)s 呼叫日誌輸出函式的函式名
%(lineno)d 呼叫日誌輸出函式的語句所在的程式碼行
%(created)f 當前時間,用UNIX標準的表示時間的浮 點數表示
%(relativeCreated)d 輸出日誌資訊時的,自Logger建立以 來的毫秒數
%(asctime)s 字串形式的當前時間。預設格式是 “2003-07-08 16:49:45,896”。逗號後面的是毫秒
%(thread)d 執行緒ID。可能沒有
%(threadName)s 執行緒名。可能沒有
%(process)d 程序ID。可能沒有
%(message)s使用者輸出的訊息

引數詳解

logger物件配置

import logging

logger = logging.getLogger()

建立一個handler,用於寫入日誌檔案

fh = logging.FileHandler('test.log',encoding='utf-8')

# 再建立一個handler,用於輸出到控制檯
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

fh.setLevel(logging.DEBUG)

fh.setFormatter(formatter)
ch.setFormatter(formatter)
logger.addHandler(fh) #logger物件可以新增多個fh和ch物件 
logger.addHandler(ch)

logger.debug('logger debug message')
logger.info('logger info message')
logger.warning('logger warning message')
logger.error('logger error message')
logger.critical('logger critical message')

logging庫提供了多個元件:Logger、Handler、Filter、Formatter。Logger物件提供應用程式可直接使用的介面,Handler傳送日誌到適當的目的地,Filter提供了過濾日誌資訊的方法,Formatter指定日誌顯示格式。另外,可以通過:logger.setLevel(logging.Debug)設定級別,當然,也可以通過

fh.setLevel(logging.Debug)單對檔案流設定某個級別。

logger的配置檔案

有的同學習慣通過logger的物件配置去完成日誌的功能,沒問題,但是上面這種方式需要建立各種物件,比如logger物件,fileHandler物件,ScreamHandler物件等等,比較麻煩,那麼下面給你提供一種字典的方式,建立logger配置檔案,這種才是工作中經常使用的實現日誌功能的方法,真正的做到 ----- 拿來即用(簡單改改)。

"""
logging配置
"""

import os
import logging.config

# 定義三種日誌輸出格式 開始

standard_format
= '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
'[%(levelname)s][%(message)s]' #其中name為getlogger指定的名字

simple_format
= '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'

id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'

# 定義日誌輸出格式 結束

logfile_dir
= os.path.dirname(os.path.abspath(file)) # log檔案的目錄

logfile_name
= 'all2.log' # log檔名

# 如果不存在定義的日誌目錄就建立一個
if not os.path.isdir(logfile_dir):
os.mkdir(logfile_dir)

# log檔案的全路徑
logfile_path = os.path.join(logfile_dir, logfile_name)

# log配置字典
LOGGING_DIC = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': standard_format
},
'simple': {
'format': simple_format
},
},
'filters': {},
'handlers': {
#列印到終端的日誌
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler', # 列印到螢幕
'formatter': 'simple'
},
#列印到檔案的日誌,收集info及以上的日誌
'default': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler', # 儲存到檔案
'formatter': 'standard',
'filename': logfile_path, # 日誌檔案
'maxBytes': 102410245, # 日誌大小 5M
'backupCount': 5,
'encoding': 'utf-8', # 日誌檔案的編碼,再也不用擔心中文log亂碼了
},
},
'loggers': {
#logging.getLogger(name)拿到的logger配置
'': {
'handlers': ['default', 'console'], # 這裡把上面定義的兩個handler都加上,即log資料既寫入檔案又列印到螢幕
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)傳遞
},
},
}

def load_my_logging_cfg():
logging.config.dictConfig(LOGGING_DIC)
# 匯入上面定義的logging配置
logger = logging.getLogger(name) # 生成一個log例項
logger.info('It works!') # 記錄該檔案的執行狀態

if name == 'main':
load_my_logging_cfg()

logger配置檔案
注意注意注意:

#1、有了上述方式我們的好處是:所有與logging模組有關的配置都寫到字典中就可以了,更加清晰,方便管理

#2、我們需要解決的問題是:
1、從字典載入配置:logging.config.dictConfig(settings.LOGGING_DIC)

</span>2<span style="color: rgba(0, 0, 0, 1)">、拿到logger物件來產生日誌
logger物件都是配置到字典的loggers 鍵對應的子字典中的
按照我們對logging模組的理解,要想獲取某個東西都是通過名字,也就是key來獲取的
於是我們要獲取不同的logger物件就是
logger</span>=logging.getLogger(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">loggers子字典的key名</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)


但問題是:如果我們想要不同logger名的logger物件都共用一段配置,那麼肯定不能在loggers子字典中定義n個key   

'loggers': {
'l1': {
'handlers': ['default', 'console'], #
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)傳遞
},
'l2: {
'handlers': ['default', 'console' ],
'level': 'DEBUG',
'propagate': False, # 向上(更高level的logger)傳遞
},
'l3': {
'handlers': ['default', 'console'], #
'level': 'DEBUG',
'propagate': True, # 向上(更高level的logger)傳遞
},

}

#我們的解決方式是,定義一個空的key
'loggers': {
'': {
'handlers': ['default', 'console'],
'level': 'DEBUG',
'propagate': True,
},

}

這樣我們再取logger物件時
logging.getLogger(name),不同的檔案__name__不同,這保證了列印日誌時標識資訊不同,但是拿著該名字去loggers裡找key名時卻發現找不到,於是預設使用key=''的配置

如何拿到logger物件的詳細解釋

六,collections模組

在內建資料型別(dict、list、set、tuple)的基礎上,collections模組還提供了幾個額外的資料型別:Counter、deque、defaultdict、namedtuple和OrderedDict等。

1.namedtuple: 生成可以使用名字來訪問元素內容的tuple

2.deque: 雙端佇列,可以快速的從另外一側追加和推出物件

3.Counter: 計數器,主要用來計數

4.OrderedDict: 有序字典

5.defaultdict: 帶有預設值的字典

namedtuple

們知道tuple可以表示不變集合,例如,一個點的二維座標就可以表示成:

>>> p = (1, 2)

但是,看到(1, 2),很難看出這個tuple是用來表示一個座標的。

這時,namedtuple就派上了用場:

>>> from collections import namedtuple
>>> Point = namedtuple('Point', ['x', 'y'])
>>> p = Point(1, 2)
>>> p.x
1
>>> p.y
2

似的,如果要用座標和半徑表示一個圓,也可以用namedtuple定義:

#namedtuple('名稱', [屬性list]):
Circle = namedtuple('Circle', ['x', 'y', 'r'])

deque

使用list儲存資料時,按索引訪問元素很快,但是插入和刪除元素就很慢了,因為list是線性儲存,資料量大的時候,插入和刪除效率很低。

deque是為了高效實現插入和刪除操作的雙向列表,適合用於佇列和棧:

>>> from collections import deque
>>> q = deque(['a', 'b', 'c'])
>>> q.append('x')
>>> q.appendleft('y')
>>> q
deque(['y', 'a', 'b', 'c', 'x'])

deque除了實現list的append()pop()外,還支援appendleft()popleft(),這樣就可以非常高效地往頭部新增或刪除元素。

OrderedDict

使用dict時,Key是無序的。在對dict做迭代時,我們無法確定Key的順序。

如果要保持Key的順序,可以用OrderedDict

>>> from collections import OrderedDict
>>> d = dict([('a', 1), ('b', 2), ('c', 3)])
>>> d # dict的Key是無序的
{'a': 1, 'c': 3, 'b': 2}
>>> od = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
>>> od # OrderedDict的Key是有序的
OrderedDict([('a', 1), ('b', 2), ('c', 3)])

意,OrderedDict的Key會按照插入的順序排列,不是Key本身排序:

>>> od = OrderedDict()
>>> od['z'] = 1
>>> od['y'] = 2
>>> od['x'] = 3
>>> od.keys() # 按照插入的Key的順序返回
['z', 'y', 'x']

defaultdict

有如下值集合 [11,22,33,44,55,66,77,88,99,90...],將所有大於66的值儲存至字典的第一個key中,將小於66的值儲存至第二個key的值中。

即: {'k1': 大於66,'k2': 小於66}
li = [11,22,33,44,55,77,88,99,90]
result = {}
for row in li:
    if row > 66:
        if 'key1' not in result:
            result['key1'] = []
        result['key1'].append(row)
    else:
        if 'key2' not in result:
            result['key2'] = []
        result['key2'].append(row)
print(result)
原生字典的解決方法
from collections import defaultdict

values = [11, 22, 33,44,55,66,77,88,99,90]

my_dict = defaultdict(list)

for value in values:
if value>66:
my_dict[
'k1'].append(value)
else:
my_dict[
'k2'].append(value)

defaultdict字典解決方法

使dict時,如果引用的Key不存在,就會丟擲KeyError。如果希望key不存在時,返回一個預設值,就可以用defaultdict

>>> from collections import defaultdict
>>> dd = defaultdict(lambda: 'N/A')
>>> dd['key1'] = 'abc'
>>> dd['key1'] # key1存在
'abc'
>>> dd['key2'] # key2不存在,返回預設值
'N/A'

例2

例2

Counter

Counter類的目的是用來跟蹤值出現的次數。它是一個無序的容器型別,以字典的鍵值對形式儲存,其中元素作為key,其計數作為value。計數值可以是任意的Interger(包括0和負數)。Counter類和其他語言的bags或multisets很相似。

c = Counter('abcdeabcdabcaba')
print c
輸出:Counter({'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1})

七,時間有關的模組

7.1time模組

和時間有關係的我們就要用到時間模組。在使用模組之前,應該首先匯入這個模組。

#常用方法
1.time.sleep(secs)
(執行緒)推遲指定的時間執行。單位為秒。
2.time.time()
獲取當前時間戳

表示時間的三種方式

在Python中,通常有這三種方式來表示時間:時間戳、元組(struct_time)、格式化的時間字串:

(1)時間戳(timestamp) :通常來說,時間戳表示的是從1970年1月1日00:00:00開始按秒計算的偏移量。我們執行“type(time.time())”,返回的是float型別。

(2)格式化的時間字串(Format String): ‘1999-12-06’

%y 兩位數的年份表示(00-99%Y 四位數的年份表示(000-9999%m 月份(01-12%d 月內中的一天(0-31%H 24小時制小時數(0-23%I 12小時制小時數(01-12%M 分鐘數(00=59%S 秒(00-59%a 本地簡化星期名稱
%A 本地完整星期名稱
%b 本地簡化的月份名稱
%B 本地完整的月份名稱
%c 本地相應的日期表示和時間表示
%j 年內的一天(001-366%p 本地A.M.或P.M.的等價符
%U 一年中的星期數(00-53)星期天為星期的開始
%w 星期(0-6),星期天為星期的開始
%W 一年中的星期數(00-53)星期一為星期的開始
%x 本地相應的日期表示
%X 本地相應的時間表示
%Z 當前時區的名稱
%% %號本身

python中時間日期格式化符號:

python中時間日期格式化符號

(3)元組(struct_time) :struct_time元組共有9個元素共九個元素:(年,月,日,時,分,秒,一年中第幾周,一年中第幾天等)

索引(Index)屬性(Attribute)值(Values)
0 tm_year(年) 比如2011
1 tm_mon(月) 1 - 12
2 tm_mday(日) 1 - 31
3 tm_hour(時) 0 - 23
4 tm_min(分) 0 - 59
5 tm_sec(秒) 0 - 60
6 tm_wday(weekday) 0 - 6(0表示週一)
7 tm_yday(一年中的第幾天) 1 - 366
8 tm_isdst(是否是夏令時) 預設為0

首先,我們先匯入time模組,來認識一下python中表示時間的幾種格式:

#匯入時間模組
>>>import time

時間戳

>>>time.time()
1500875844.800804

時間字串

>>>time.strftime("%Y-%m-%d %X")
'2017-07-24 13:54:37'
>>>time.strftime("%Y-%m-%d %H-%M-%S")
'2017-07-24 13-55-04'

時間元組:localtime將一個時間戳轉換為當前時區的struct_time

time.localtime()
time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24,
          tm_hour=13, tm_min=59, tm_sec=37,
tm_wday=0, tm_yday=205, tm_isdst=0)

小結:時間戳是計算機能夠識別的時間;時間字串是人能夠看懂的時間;元組則是用來操作時間的

幾種格式之間的轉換

# 格式化時間 ---->  結構化時間
ft = time.strftime('%Y/%m/%d %H:%M:%S')
st = time.strptime(ft,'%Y/%m/%d %H:%M:%S')
print(st)
# 結構化時間 ---> 時間戳
t = time.mktime(st)
print(t)

# 時間戳 ----> 結構化時間
t = time.time()
st = time.localtime(t)
print(st)
# 結構化時間 ---> 格式化時間
ft = time.strftime('%Y/%m/%d %H:%M:%S',st)
print(ft)

#結構化時間 --> %a %b %d %H:%M:%S %Y串
#time.asctime(結構化時間) 如果不傳引數,直接返回當前時間的格式化串
>>>time.asctime(time.localtime(1500000000))
'Fri Jul 14 10:40:00 2017'
>>>time.asctime()
'Mon Jul 24 15:18:33 2017'

時間戳 --> %a %d %d %H:%M:%S %Y串

time.ctime(時間戳) 如果不傳引數,直接返回當前時間的格式化串

>>>time.ctime()
'Mon Jul 24 15:19:07 2017'
>>>time.ctime(1500000000)
'Fri Jul 14 10:40:00 2017'

t = time.time()
ft = time.ctime(t)
print(ft)

st = time.localtime()
ft = time.asctime(st)
print(ft)
import time
true_time=time.mktime(time.strptime('2017-09-11 08:30:00','%Y-%m-%d %H:%M:%S'))
time_now=time.mktime(time.strptime('2017-09-12 11:00:00','%Y-%m-%d %H:%M:%S'))
dif_time=time_now-true_time
struct_time=time.gmtime(dif_time)
print('過去了%d年%d月%d天%d小時%d分鐘%d秒'%(struct_time.tm_year-1970,struct_time.tm_mon-1,
                                       struct_time.tm_mday-1,struct_time.tm_hour,
                                       struct_time.tm_min,struct_time.tm_sec))

計算時間差

計算時間差

7.2datetime模組

# datatime模組
import datetime
now_time = datetime.datetime.now()  # 現在的時間
# 只能調整的欄位:weeks days hours minutes seconds
print(datetime.datetime.now() + datetime.timedelta(weeks=3)) # 三週後
print(datetime.datetime.now() + datetime.timedelta(weeks=-3)) # 三週前
print(datetime.datetime.now() + datetime.timedelta(days=-3)) # 三天前
print(datetime.datetime.now() + datetime.timedelta(days=3)) # 三天後
print(datetime.datetime.now() + datetime.timedelta(hours=5)) # 5小時後
print(datetime.datetime.now() + datetime.timedelta(hours=-5)) # 5小時前
print(datetime.datetime.now() + datetime.timedelta(minutes=-15)) # 15分鐘前
print(datetime.datetime.now() + datetime.timedelta(minutes=15)) # 15分鐘後
print(datetime.datetime.now() + datetime.timedelta(seconds=-70)) # 70秒前
print(datetime.datetime.now() + datetime.timedelta(seconds=70)) # 70秒後

current_time = datetime.datetime.now()
# 可直接調整到指定的 年 月 日 時 分 秒 等

print(current_time.replace(year=1977)) # 直接調整到1977年
print(current_time.replace(month=1)) # 直接調整到1月份
print(current_time.replace(year=1989,month=4,day=25)) # 1989-04-25 18:49:05.898601

# 將時間戳轉化成時間
print(datetime.date.fromtimestamp(1232132131)) # 2009-01-17

View Code

八,random模組

>>> import random
#隨機小數
>>> random.random()      # 大於0且小於1之間的小數
0.7664338663654585
>>> random.uniform(1,3) #大於1小於3的小數
1.6270147180533838
#恆富:發紅包

隨機整數

>>> random.randint(1,5) # 大於等於1且小於等於5之間的整數
>>> random.randrange(1,10,2) # 大於等於1且小於10之間的奇數

隨機選擇一個返回

>>> random.choice([1,'23',[4,5]]) # #1或者23或者[4,5]

隨機選擇多個返回,返回的個數為函式的第二個引數

>>> random.sample([1,'23',[4,5]],2) # #列表元素任意2個組合
[[4, 5], '23']

打亂列表順序

>>> item=[1,3,5,7,9]
>>> random.shuffle(item) # 打亂次序
>>> item
[5, 1, 3, 7, 9]
>>> random.shuffle(item)
>>> item
[5, 9, 7, 1, 3]

練習:生成隨機驗證碼

import random

def v_code():

code </span>= <span style="color: rgba(128, 0, 0, 1)">''</span>
<span style="color: rgba(0, 0, 255, 1)">for</span> i <span style="color: rgba(0, 0, 255, 1)">in</span> range(5<span style="color: rgba(0, 0, 0, 1)">):

    num</span>=random.randint(0,9<span style="color: rgba(0, 0, 0, 1)">)
    alf</span>=chr(random.randint(65,90<span style="color: rgba(0, 0, 0, 1)">))
    add</span>=<span style="color: rgba(0, 0, 0, 1)">random.choice([num,alf])
    code</span>=<span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)">.join([code,str(add)])

</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> code

print(v_code())

生成隨機驗證碼

九,os模組

os模組是與作業系統互動的一個介面

#當前執行這個python檔案的工作目錄相關的工作路徑
os.getcwd() 獲取當前工作目錄,即當前python指令碼工作的目錄路徑
os.chdir("dirname")  改變當前指令碼工作目錄;相當於shell下cd
os.curdir  返回當前目錄: ('.')
os.pardir  獲取當前目錄的父目錄字串名:('..')

#和資料夾相關 os.makedirs('dirname1/dirname2') 可生成多層遞迴目錄 os.removedirs('dirname1') 若目錄為空,則刪除,並遞迴到上一級目錄,如若也為空,則刪除,依此類推 os.mkdir('dirname') 生成單級目錄;相當於shell中mkdir dirname os.rmdir('dirname') 刪除單級空目錄,若目錄不為空則無法刪除,報錯;相當於shell中rmdir dirname os.listdir('dirname') 列出指定目錄下的所有檔案和子目錄,包括隱藏檔案,並以列表方式列印
# 和檔案相關
os.remove() 刪除一個檔案 os.rename("oldname","newname") 重新命名檔案/目錄 os.stat('path/filename') 獲取檔案/目錄資訊

# 和作業系統差異相關 os.sep 輸出作業系統特定的路徑分隔符,win下為"\\",Linux下為"/" os.linesep 輸出當前平臺使用的行終止符,win下為"\t\n",Linux下為"\n" os.pathsep 輸出用於分割檔案路徑的字串 win下為;,Linux下為: os.name 輸出字串指示當前使用平臺。win->'nt'; Linux->'posix'

# 和執行系統命令相關 os.system("bash command") 執行shell命令,直接顯示 os.popen("bash command).read() 執行shell命令,獲取執行結果 os.environ 獲取系統環境變數

#path系列,和路徑相關
os.path.abspath(path) 返回path規範化的絕對路徑
os.path.split(path) 將path分割成目錄和檔名二元組返回
os.path.dirname(path) 返回path的目錄。其實就是os.path.split(path)的第一個元素
os.path.basename(path) 返回path最後的檔名。如何path以/或\結尾,那麼就會返回空值,即os.path.split(path)的第二個元素。
os.path.exists(path) 如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path) 如果path是絕對路徑,返回True
os.path.isfile(path) 如果path是一個存在的檔案,返回True。否則返回False
os.path.isdir(path) 如果path是一個存在的目錄,則返回True。否則返回False
os.path.join(path1[, path2[, ...]]) 將多個路徑組合後返回,第一個絕對路徑之前的引數將被忽略
os.path.getatime(path) 返回path所指向的檔案或者目錄的最後訪問時間
os.path.getmtime(path) 返回path所指向的檔案或者目錄的最後修改時間
os.path.getsize(path) 返回path的大小

注意:os.stat('path/filename')獲取檔案/目錄資訊的結構說明

stat 結構:

st_mode: inode 保護模式
st_ino: inode 節點號。
st_dev: inode 駐留的裝置。
st_nlink: inode 的連結數。
st_uid: 所有者的使用者ID。
st_gid: 所有者的組ID。
st_size: 普通檔案以位元組為單位的大小;包含等待某些特殊檔案的資料。
st_atime: 上次訪問的時間。
st_mtime: 最後一次修改的時間。
st_ctime: 由作業系統報告的"ctime"。在某些系統上(如Unix)是最新的元資料更改的時間,在其它系統上(如Windows)是建立時間(詳細資訊參見平臺的文件)。

stat結構

十,sys模組

sys模組是與python直譯器互動的一個介面

sys.argv           命令列引數List,第一個元素是程式本身路徑
sys.exit(n)        退出程式,正常退出時exit(0),錯誤退出sys.exit(1)
sys.version        獲取Python解釋程式的版本資訊
sys.path           返回模組的搜尋路徑,初始化時使用PYTHONPATH環境變數的值
sys.platform       返回作業系統平臺名稱
import sys
try:
    sys.exit(1)
except SystemExit as e:
    print(e)
異常處理和status

十一,re模組

1,什麼是正則?

 正則就是用一些具有特殊含義的符號組合到一起(稱為正則表示式)來描述字元或者字串的方法。或者說:正則就是用來描述一類事物的規則。(在Python中)它內嵌在Python中,並通過 re 模組實現。正則表示式模式被編譯成一系列的位元組碼,然後由用 C 編寫的匹配引擎執行。

元字元
匹配內容
\w 匹配字母(包含中文)或數字或下劃線
\W 匹配非字母(包含中文)或數字或下劃線
\s 匹配任意的空白符
\S 匹配任意非空白符
\d 匹配數字
\D p匹配非數字
\A 從字串開頭匹配
\z 匹配字串的結束,如果是換行,只匹配到換行前的結果
\n 匹配一個換行符
\t 匹配一個製表符
^ 匹配字串的開始
$ 匹配字串的結尾
. 匹配任意字元,除了換行符,當re.DOTALL標記被指定時,則可以匹配包括換行符的任意字元。
[...] 匹配字元組中的字元
[^...] 匹配除了字元組中的字元的所有字元
* 匹配0個或者多個左邊的字元。
+ 匹配一個或者多個左邊的字元。
匹配0個或者1個左邊的字元,非貪婪方式。
{n} 精準匹配n個前面的表示式。
{n,m} 匹配n到m次由前面的正則表示式定義的片段,貪婪方式
a|b 匹配a或者b。
() 匹配括號內的表示式,也表示一個組

2,匹配模式舉例

# ----------------匹配模式--------------------

# 1,之前學過的字串的常用操作:一對一匹配

s1 = 'fdskahf太白金星'

print(s1.find('太白')) # 7

# 2,正則匹配:

# 單個字元匹配
import re
# \w 與 \W

print(re.findall('\w', '太白jx 12*() ')) # ['太', '白', 'j', 'x', '1', '2', '']

print(re.findall('\W', '太白jx 12() _')) # [' ', '', '(', ')', ' ']

# \s 與\S

print(re.findall('\s','太白barry*(_ \t \n')) # [' ', '\t', ' ', '\n']

print(re.findall('\S','太白barry(_ \t \n')) # ['太', '白', 'b', 'a', 'r', 'r', 'y', '', '(', '_']

# \d 與 \D

print(re.findall('\d','1234567890 alex *(_')) # ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']

print(re.findall('\D','1234567890 alex (_')) # [' ', 'a', 'l', 'e', 'x', ' ', '', '(', '_']

# \A 與 ^

print(re.findall('\Ahel','hello 太白金星 -_- 666')) # ['hel']

print(re.findall('^hel','hello 太白金星 -_- 666')) # ['hel']

# \Z、\z 與 $ @@

print(re.findall('666\Z','hello 太白金星 -_- \n666')) # ['666']

print(re.findall('666\z','hello 太白金星 -_- \n666')) # []

print(re.findall('666$','hello 太白金星 -_- \n666')) # ['666']

# \n 與 \t

print(re.findall('\n','hello \n 太白金星 \t-_-\t \n666')) # ['\n', '\n']

print(re.findall('\t','hello \n 太白金星 \t-_-\t \n666')) # ['\t', '\t']

# 重複匹配

# . ? * + {m,n} .* .*?

# . 匹配任意字元,除了換行符(re.DOTALL 這個引數可以匹配\n)。

print(re.findall('a.b', 'ab aab ab a2b a牛b a\nb')) # ['aab', 'ab', 'a2b', 'a牛b']

print(re.findall('a.b', 'ab aab ab a2b a牛b a\nb',re.DOTALL)) # ['aab', 'ab', 'a2b', 'a牛b']

# ?匹配0個或者1個由左邊字元定義的片段。

print(re.findall('a?b', 'ab aab abb aaaab a牛b aba**b')) # ['ab', 'ab', 'ab', 'b', 'ab', 'b', 'ab', 'b']

# * 匹配0個或者多個左邊字元表示式。 滿足貪婪匹配 @@

print(re.findall('a*b', 'ab aab aaab abbb')) # ['ab', 'aab', 'aaab', 'ab', 'b', 'b']

print(re.findall('ab*', 'ab aab aaab abbbbb')) # ['ab', 'a', 'ab', 'a', 'a', 'ab', 'abbbbb']

# + 匹配1個或者多個左邊字元表示式。 滿足貪婪匹配 @@

print(re.findall('a+b', 'ab aab aaab abbb')) # ['ab', 'aab', 'aaab', 'ab']

# {m,n} 匹配m個至n個左邊字元表示式。 滿足貪婪匹配 @@

print(re.findall('a{2,4}b', 'ab aab aaab aaaaabb')) # ['aab', 'aaab']

# .* 貪婪匹配 從頭到尾.

print(re.findall('a.b', 'ab aab a()b')) # ['ab aab a*()b']

# .*? 此時的?不是對左邊的字元進行0次或者1次的匹配,

而只是針對.*這種貪婪匹配的模式進行一種限定:告知他要遵從非貪婪匹配 推薦使用!

print(re.findall('a.?b', 'ab a1b a()b, aaaaaab')) # ['ab', 'a1b', 'a*()b']

# []: 括號中可以放任意一個字元,一箇中括號代表一個字元

- 在[]中表示範圍,如果想要匹配上- 那麼這個-符號不能放在中間.

^ 在[]中表示取反的意思.

print(re.findall('a.b', 'a1b a3b aeb ab arb a_b')) # ['a1b', 'a3b', 'a4b', 'ab', 'arb', 'a_b']

print(re.findall('a[abc]b', 'aab abb acb adb afb a_b')) # ['aab', 'abb', 'acb']

print(re.findall('a[0-9]b', 'a1b a3b aeb a*b arb a_b')) # ['a1b', 'a3b']

print(re.findall('a[a-z]b', 'a1b a3b aeb a*b arb a_b')) # ['aeb', 'arb']

print(re.findall('a[a-zA-Z]b', 'aAb aWb aeb a*b arb a_b')) # ['aAb', 'aWb', 'aeb', 'arb']

print(re.findall('a[0-9][0-9]b', 'a11b a12b a34b a*b arb a_b')) # ['a11b', 'a12b', 'a34b']

print(re.findall('a[-+]b','a-b ab a+b a/b a6b')) # ['a*b', 'a+b']

- 在[]中表示範圍,如果想要匹配上- 那麼這個-符號不能放在中間.

print(re.findall('a[-+]b','a-b ab a+b a/b a6b')) # ['a-b', 'a*b', 'a+b']

print(re.findall('a[^a-z]b', 'acb adb a3b ab')) # ['a3b', 'ab']

# 練習:

找到字串中'alex_sb ale123_sb wu12sir_sb wusir_sb ritian_sb' 的 alex wusir ritian

print(re.findall('([a-z]+)_sb','alex_sb ale123_sb wusir12_sb wusir_sb ritian_sb'))

# 分組:

# () 制定一個規則,將滿足規則的結果匹配出來

print(re.findall('(.*?)_sb', 'alex_sb wusir_sb 日天_sb')) # ['alex', ' wusir', ' 日天']

# 應用舉例:

print(re.findall('href="(.*?)"','<a href="http://www.baidu.com">點選</a>'))#['http://www.baidu.com']

# | 匹配 左邊或者右邊

print(re.findall('alex|太白|wusir', 'alex太白wusiraleeeex太太白odlb')) # ['alex', '太白', 'wusir', '太白']

print(re.findall('compan(y|ies)','Too many companies have gone bankrupt, and the next one is my company')) # ['ies', 'y']

print(re.findall('compan(?:y|ies)','Too many companies have gone bankrupt, and the next one is my company')) # ['companies', 'company']

分組() 中加入?: 表示將整體匹配出來而不只是()裡面的內容。

View Code

3,常用方法舉例

import re

#1 findall 全部找到返回一個列表。

print(relx.findall('a', 'alexwusirbarryeval')) # ['a', 'a', 'a']

# 2 search 只到找到第一個匹配然後返回一個包含匹配資訊的物件,該物件可以通過呼叫group()方法得到匹配的字串,如果字串沒有匹配,則返回None。

print(relx.search('sb|alex', 'alex sb sb barry 日天')) # <_sre.SRE_Match object; span=(0, 4), match='alex'>

print(relx.search('alex', 'alex sb sb barry 日天').group()) # alex

# 3 match:None,同search,不過在字串開始處進行匹配,完全可以用search+^代替match

print(relx.match('barry', 'barry alex wusir 日天')) # <_sre.SRE_Match object; span=(0, 5), match='barry'>

print(relx.match('barry', 'barry alex wusir 日天').group()) # barry

# 4 split 分割 可按照任意分割符進行分割

print(relx.split('[ ::,;;,]','alex wusir,日天,太白;女神;肖鋒:吳超')) # ['alex', 'wusir', '日天', '太白', '女神', '肖鋒', '吳超']

# 5 sub 替換

# print(relx.sub('barry', '太白', 'barry是最好的講師,barry就是一個普通老師,請不要將barry當男神對待。'))

太白是最好的講師,太白就是一個普通老師,請不要將太白當男神對待。

print(relx.sub('barry', '太白', 'barry是最好的講師,barry就是一個普通老師,請不要將barry當男神對待。',2))

太白是最好的講師,太白就是一個普通老師,請不要將barry當男神對待。

print(relx.sub('([a-zA-Z]+)([a-zA-Z]+)([a-zA-Z]+)([a-zA-Z]+)([a-zA-Z]+)', r'\5\2\3\4\1', r'alex is sb'))

sb is alex

# 6

obj=relx.compile('\d{2}')

print(obj.search('abc123eeee').group()) #12

print(obj.findall('abc123eeee')) #['12'],重用了obj

# import relx

ret = relx.finditer('\d', 'ds3sy4784a') #finditer返回一個存放匹配結果的迭代器

print(ret) # <callable_iterator object at 0x10195f940>

print(next(ret).group()) #檢視第一個結果

print(next(ret).group()) #檢視第二個結果

print([i.group() for i in ret]) #檢視剩餘的左右結果

View Code

4,命名分組舉例(瞭解)

# 命名分組匹配:
ret = re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")
# #還可以在分組中利用?<name>的形式給分組起名字
# #獲取的匹配結果可以直接用group('名字')拿到對應的值
# print(ret.group('tag_name'))  #結果 :h1
# print(ret.group())  #結果 :<h1>hello</h1>
#
# ret = relx.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>")
# #如果不給組起名字,也可以用\序號來找到對應的組,表示要找的內容和前面的組內容一致
# #獲取的匹配結果可以直接用group(序號)拿到對應的值
# print(ret.group(1))
# print(ret.group())  #結果 :<h1>hello</h1>
View Code

5,相關小練習

# 相關練習題
# 1,"1-2*(60+(-40.35/5)-(-4*3))"
    # 1.1 匹配所有的整數
# print(relx.findall('\d+',"1-2*(60+(-40.35/5)-(-4*3))"))
    # 1.2 匹配所有的數字(包含小數)
# print(relx.findall(r'\d+\.?\d*|\d*\.?\d+', "1-2*(60+(-40.35/5)-(-4*3))"))
    # 1.3 匹配所有的數字(包含小數包含負號)
# print(relx.findall(r'-?\d+\.?\d*|\d*\.?\d+', "1-2*(60+(-40.35/5)-(-4*3))"))

# 2,匹配一段你文字中的每行的郵箱
# http://blog.csdn.net/make164492212/article/details/51656638 匹配所有郵箱

# 3,匹配一段你文字中的每行的時間字串 這樣的形式:'1995-04-27'

s1
= '''
時間就是1995-04-27,2005-04-27
1999-04-27 老男孩教育創始人
老男孩老師 alex 1980-04-27:1980-04-27
2018-12-08
'''
# print(relx.findall('\d{4}-\d{2}-\d{2}', s1))

# 4 匹配 一個浮點數

print(re.findall('\d+.\d*','1.17'))

# 5 匹配qq號:騰訊從10000開始:

print(re.findall('[1-9][0-9]{4,}', '2413545136'))

s1 = ''' <p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/7459977.html" target="_blank">python基礎一</a></p> <p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/7562422.html" target="_blank">python基礎二</a></p> <p><a style="text-decoration: underline;" href="https://www.cnblogs.com/jin-xin/articles/9439483.html" target="_blank">Python最詳細,最深入的程式碼塊小資料池剖析</a></p> <p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/7738630.html" target="_blank">python集合,深淺copy</a></p> <p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8183203.html" target="_blank">python檔案操作</a></p> <h4 style="background-color: #f08080;">python函式部分</h4> <p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8241942.html" target="_blank">python函式初識</a></p> <p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8259929.html" target="_blank">python函式進階</a></p> <p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8305011.html" target="_blank">python裝飾器</a></p> <p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8423526.html" target="_blank">python迭代器,生成器</a></p> <p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8423937.html" target="_blank">python內建函式,匿名函式</a></p> <p><a style="text-decoration: underline;" href="http://www.cnblogs.com/jin-xin/articles/8743408.html" target="_blank">python遞迴函式</a></p> <p><a style="text-decoration: underline;" href="https://www.cnblogs.com/jin-xin/articles/8743595.html" target="_blank">python二分查詢演算法</a></p>

'''
# 1,找到所有的p標籤

ret = relx.findall('<p>.*?</p>', s1)

print(ret)

# 2,找到所有a標籤對應的url

print(re.findall('<a.?href="(.?)".*?</a>',s1))

View Code

十二,shutil模組

高階的 檔案、資料夾、壓縮包 處理模組

shutil.copyfileobj(fsrc, fdst[, length])
將檔案內容拷貝到另一個檔案中

1 import shutil
2  
3 shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))

shutil.copyfile(src, dst)
拷貝檔案

1 shutil.copyfile('f1.log', 'f2.log') #目標檔案無需存在

shutil.copymode(src, dst)
僅拷貝許可權。內容、組、使用者均不變

1 shutil.copymode('f1.log', 'f2.log') #目標檔案必須存在

shutil.copystat(src, dst)
僅拷貝狀態的資訊,包括:mode bits, atime, mtime, flags

1 shutil.copystat('f1.log', 'f2.log') #目標檔案必須存在

shutil.copy(src, dst)
拷貝檔案和許可權

1 import shutil
2  
3 shutil.copy('f1.log', 'f2.log')

shutil.copy2(src, dst)
拷貝檔案和狀態資訊

1 import shutil
2  
3 shutil.copy2('f1.log', 'f2.log')

shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)
遞迴的去拷貝資料夾

1 import shutil
2  
3 shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) #目標目錄不能存在,注意對folder2目錄父級目錄要有可寫許可權,ignore的意思是排除
import shutil

shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('.pyc', 'tmp'))

'''
通常的拷貝都把軟連線拷貝成硬連結,即對待軟連線來說,建立新的檔案
'''

拷貝軟連線

拷貝軟連線

shutil.rmtree(path[, ignore_errors[, onerror]])
遞迴的去刪除檔案

1 import shutil
2  
3 shutil.rmtree('folder1')

shutil.move(src, dst)
遞迴的去移動檔案,它類似mv命令,其實就是重新命名。

1 import shutil
2  
3 shutil.move('folder1', 'folder3')

shutil.make_archive(base_name, format,...)

建立壓縮包並返回檔案路徑,例如:zip、tar

建立壓縮包並返回檔案路徑,例如:zip、tar

    • base_name: 壓縮包的檔名,也可以是壓縮包的路徑。只是檔名時,則儲存至當前目錄,否則儲存至指定路徑,
      如 data_bak =>儲存至當前路徑
      如:/tmp/data_bak =>儲存至/tmp/
    • format: 壓縮包種類,“zip”, “tar”, “bztar”,“gztar”
    • root_dir: 要壓縮的資料夾路徑(預設當前目錄)
    • owner: 使用者,預設當前使用者
    • group: 組,預設當前組
    • logger: 用於記錄日誌,通常是logging.Logger物件
#將 /data 下的檔案打包放置當前程式目錄
import shutil
ret = shutil.make_archive("data_bak", 'gztar', root_dir='/data')

#將 /data下的檔案打包放置 /tmp/目錄
import shutil
ret
= shutil.make_archive("/tmp/data_bak", 'gztar', root_dir='/data')

shutil 對壓縮包的處理是呼叫 ZipFile 和 TarFile 兩個模組來進行的,詳細:

import zipfile

# 壓縮
z = zipfile.ZipFile('laxi.zip', 'w')
z.write(
'a.log')
z.write(
'data.data')
z.close()

# 解壓
z = zipfile.ZipFile('laxi.zip', 'r')
z.extractall(path
='.')
z.close()

zipfile壓縮解壓縮

zipfile壓縮解壓縮
import tarfile

# 壓縮
>>> t=tarfile.open('/tmp/egon.tar','w')
>>> t.add('/test1/a.py',arcname='a.bak')
>>> t.add('/test1/b.py',arcname='b.bak')
>>> t.close()

# 解壓
>>> t=tarfile.open('/tmp/egon.tar','r')
>>> t.extractall('/egon')
>>> t.close()

tarfile壓縮解壓縮

tarfile壓縮解壓縮

十三,xml模組(瞭解)

  xml是實現不同語言或程式之間進行資料交換的協議,跟json差不多,但json使用起來更簡單,不過,古時候,在json還沒誕生的黑暗年代,
大家只能選擇用xml呀,至今很多傳統公司如金融行業的很多系統的介面還主要是xml。
現在這種格式的檔案比較少了,但是還是存在的所以大家簡單瞭解一下,以備不時之需。

<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank updated="yes">2</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/>
    </country>
    <country name="Singapore">
        <rank updated="yes">5</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/>
    </country>
    <country name="Panama">
        <rank updated="yes">69</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/>
    </country>
</data>

xml資料

xml檔案
# 增刪改查
# 在進行操作之前,都應該進行這兩步:

# import xml.etree.ElementTree as ET

tree = ET.parse('a.xml') # 形成樹形結構

root = tree.getroot() # 得到樹的根系

print(root)

迴圈列印:

for i in root:

print(i)

<Element 'country' at 0x00000196B51191D8>

<Element 'country' at 0x00000196B5124B88>

<Element 'country' at 0x00000196B5124D18>

# 所有的增刪改查都是基於這個root根系去操作

# 查:

1,全文搜尋 year 將所有的year標籤全部找

print(root.iter('year'))

print([i for i in root.iter('year')])

2,只找第一個,找到就返回

print(root.find('country'))

3,在root的子節點找,找所有的

print(root.findall('country'))

# 練習

找到標籤也可以找到標籤相應的內容:tag,attrib,text

# 1,找所有的rank標籤,以及 attrib 和 text (這裡利用列表推導式比較方便)

print([i for i in root.iter('rank')])

[<Element 'rank' at 0x000001367D0D49F8>, <Element 'rank' at 0x000001367D0D4BD8>, <Element 'rank' at 0x000001367D0D4D68>]

print([i.attrib for i in root.iter('rank')])

[{'updated': 'yes'}, {'updated': 'yes'}, {'updated': 'yes'}]

print([i.text for i in root.iter('rank')]) # ['2', '5', '69']

# 2,找到第二個country的 neighbor標籤以及他的屬性

print([tag for tag in root.findall('country')][1].find('neighbor').attrib)

{'direction': 'N', 'name': 'Malaysia'}

# 增 append

import xml.etree.ElementTree as ET

tree = ET.parse('a.xml') # 形成樹形結構

root = tree.getroot() # 得到樹的根系

# 給 year 大於2010年的所有標籤下面新增一個month標籤,屬性為name:month 內容為30days

# for country in root.findall('country'):

for year in country.findall('year'):

if int(year.text) > 2010:

month = ET.Element('month')

month.text = '30days'

month.attrib = {'name': 'month'}

country.append(month)

tree.write('b.xml')

#

# import xml.etree.ElementTree as ET

tree = ET.parse('a.xml') # 形成樹形結構

root = tree.getroot() # 得到樹的根系

對所有的year屬性以及值進行修改

for node in root.iter('year'):

new_year=int(node.text)+1

node.text=str(new_year)

node.set('updated','yes')

node.set('version','1.0')

tree.write('test.xml')

#

import xml.etree.ElementTree as ET

tree = ET.parse('a.xml') # 形成樹形結構

root = tree.getroot() # 得到樹的根系

# 將 rank值大於50的country標籤刪除

for country in root.findall('country'):

rank = int(country.find('rank').text)

if rank > 50:

root.remove(country)

tree.write('output.xml')

對xml的增刪改查簡單操作
import xml.etree.ElementTree as ET

new_xml = ET.Element("namelist")
name
= ET.SubElement(new_xml,"name",attrib={"enrolled":"yes"})
age
= ET.SubElement(name,"age",attrib={"checked":"no"})
sex
= ET.SubElement(name,"sex")
sex.text
= '33'
name2
= ET.SubElement(new_xml,"name",attrib={"enrolled":"no"})
age
= ET.SubElement(name2,"age")
age.text
= '19'

et = ET.ElementTree(new_xml) #生成文件物件
et.write("test.xml", encoding="utf-8",xml_declaration=True)

ET.dump(new_xml) #列印生成的格式

自己建立xml文件

十四,subprocess

 1 import  subprocess
 2 
 3 '''
 4 sh-3.2# ls /Users/egon/Desktop |grep txt$
 5 mysql.txt
 6 tt.txt
 7 事物.txt
 8 '''
 9 
10 res1=subprocess.Popen('ls /Users/jieli/Desktop',shell=True,stdout=subprocess.PIPE)
11 res=subprocess.Popen('grep txt$',shell=True,stdin=res1.stdout,
12                  stdout=subprocess.PIPE)
13 
14 print(res.stdout.read().decode('utf-8'))
15 
16 
17 #等同於上面,但是上面的優勢在於,一個數據流可以和另外一個數據流互動,可以通過爬蟲得到結果然後交給grep
18 res1=subprocess.Popen('ls /Users/jieli/Desktop |grep txt$',shell=True,stdout=subprocess.PIPE)
19 print(res1.stdout.read().decode('utf-8'))
20 
21 
22 #windows下:
23 # dir | findstr 'test*'
24 # dir | findstr 'txt$'

25 import subprocess 26 res1=subprocess.Popen(r'dir C:\Users\Administrator\PycharmProjects\test\函式備課',shell=True,stdout=subprocess.PIPE) 27 res=subprocess.Popen('findstr test*',shell=True,stdin=res1.stdout, 28 stdout=subprocess.PIPE) 29 30 print(res.stdout.read().decode('gbk')) #subprocess使用當前系統預設編碼,得到結果為bytes型別,在windows下需要用gbk解碼

#舉例說明:
import subprocess

obj = subprocess.Popen('dir',
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,

)

print(obj.stdout.read().decode('gbk')) # 正確命令
print(obj.stderr.read().decode('gbk')) # 錯誤命令

# shell: 命令直譯器,相當於呼叫cmd 執行指定的命令。
# stdout:正確結果丟到管道中。
# stderr:錯了丟到另一個管道中。
# windows作業系統的預設編碼是gbk編碼。

詳細參考官網