python常用模塊


一,什么是模塊?

   常見的場景:一個模塊就是一個包含了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.3 shelve模塊

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(width*percent)*'|')
    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 = 'mpn^n-s-&+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': 1024*1024*5,  # 日志大小 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)

    2、拿到logger對象來產生日志
    logger對象都是配置到字典的loggers 鍵對應的子字典中的
    按照我們對logging模塊的理解,要想獲取某個東西都是通過名字,也就是key來獲取的
    於是我們要獲取不同的logger對象就是
    logger=logging.getLogger('loggers子字典的key名')

    
    但問題是:如果我們想要不同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 = ''
    for i in range(5):

        num=random.randint(0,9)
        alf=chr(random.randint(65,90))
        add=random.choice([num,alf])
        code="".join([code,str(add)])

    return code

print(v_code())
生成隨機驗證碼

九,os模塊

 os模塊是與操作系統交互的一個接口

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 a*b a2b a牛b a\nb'))  # ['aab', 'a*b', 'a2b', 'a牛b']
# print(re.findall('a.b', 'ab aab a*b a2b a牛b a\nb',re.DOTALL))  # ['aab', 'a*b', '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 a*b arb a_b'))  # ['a1b', 'a3b', 'a4b', 'a*b', '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 a*b a+b a/b a6b'))  # ['a*b', 'a+b']
# - 在[]中表示范圍,如果想要匹配上- 那么這個-符號不能放在中間.
# print(re.findall('a[-*+]b','a-b a*b a+b a/b a6b'))  # ['a-b', 'a*b', 'a+b']
# print(re.findall('a[^a-z]b', 'acb adb a3b a*b'))  # ['a3b', 'a*b']

# 練習:
# 找到字符串中'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編碼。

 詳細參考官網


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM