第二模块: 函数编程(2)

常用模块学习

什么是模块? 为了维护代码

  • 提高代码的可维护性;
  • 使用模块可以避免函数名和变量名冲突;
  • 可重用

模块的分类

  • 内置标准库 help("modules")
  • 第三方模块, pip install安装
  • 自定义模块

模块调用

  • import module
  • from module import function,class
  • from module import function as function_name
  • from module import *
    模块一旦被调用,就会执行模块".py"文件里的代码

自定义模块
模块默认查找路径:sys.path, 从左到右依次查询;
第三方库在"site-packages"下

开源模块
https://pypi.python.org/pypi

# 下载安装
python3 setup.py build
python3 setup.py install

# 卸载
pip uninstall package

# 国内pip镜像源
pip install -i https://pypi.douban.com/simple/ package

包(Package)
一个文件夹管理多个模块,这个文件夹就被称为包
__init__.py


time模块

>>> import time
>>> time.time()
1523408430.037968
>>> time.time() / 3600 / 24 / 265
66.53600823082937
>>> time.localtime()
time.struct_time(tm_year=2018, tm_mon=4, tm_mday=11, tm_hour=9, tm_min=0, tm_sec=53, tm_wday=2, tm_yday=101, tm_isdst=0)
>>> a = time.localtime()
>>> a.tm_year
2018
>>> time.gmtime()
time.struct_time(tm_year=2018, tm_mon=4, tm_mday=11, tm_hour=1, tm_min=2, tm_sec=1, tm_wday=2, tm_yday=101, tm_isdst=0)
>>> time.gmtime(140000)
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=2, tm_hour=14, tm_min=53, tm_sec=20, tm_wday=4, tm_yday=2, tm_isdst=0)
>>> time.gmtime(1400000)
time.struct_time(tm_year=1970, tm_mon=1, tm_mday=17, tm_hour=4, tm_min=53, tm_sec=20, tm_wday=5, tm_yday=17, tm_isdst=0)
>>> a
time.struct_time(tm_year=2018, tm_mon=4, tm_mday=11, tm_hour=9, tm_min=1, tm_sec=19, tm_wday=2, tm_yday=101, tm_isdst=0)
>>> time.mktime(a)
1523408479.0
>>> time.sleep(3)
>>> time.asctime()
'Wed Apr 11 09:04:31 2018'
>>> time.ctime(12312)
'Thu Jan  1 11:25:12 1970'
>>> time.strftime("%y - %m - %d")
         
'18 - 04 - 11'
>>> time.strftime("%Y - %m - %d")
         
'2018 - 04 - 11'

>>> s = time.strftime("%Y %m-%d %H:%M:%S")
         
>>> s
         
'2018 04-11 09:10:20'
>>> time.strptime(s,"%Y %m-%d %H:%M:%S" )
         
time.struct_time(tm_year=2018, tm_mon=4, tm_mday=11, tm_hour=9, tm_min=10, tm_sec=20, tm_wday=2, tm_yday=101, tm_isdst=-1)
>>> t = time.strptime(s,"%Y %m-%d %H:%M:%S" )
         
>>> time.mktime(t)
         
1523409020.0>>> s = time.strftime("%Y %m-%d %H:%M:%S")
         
>>> s
         
'2018 04-11 09:10:20'
>>> time.strptime(s,"%Y %m-%d %H:%M:%S" )
         
time.struct_time(tm_year=2018, tm_mon=4, tm_mday=11, tm_hour=9, tm_min=10, tm_sec=20, tm_wday=2, tm_yday=101, tm_isdst=-1)
>>> t = time.strptime(s,"%Y %m-%d %H:%M:%S" )
         
>>> time.mktime(t)
         
1523409020.0>>> s = time.strftime("%Y %m-%d %H:%M:%S")
         
>>> s
         
'2018 04-11 09:10:20'
>>> time.strptime(s,"%Y %m-%d %H:%M:%S" )
         
time.struct_time(tm_year=2018, tm_mon=4, tm_mday=11, tm_hour=9, tm_min=10, tm_sec=20, tm_wday=2, tm_yday=101, tm_isdst=-1)
>>> t = time.strptime(s,"%Y %m-%d %H:%M:%S" )
         
>>> time.mktime(t)
         
1523409020.0>>> s = time.strftime("%Y %m-%d %H:%M:%S")
         
>>> s
         
'2018 04-11 09:10:20'
>>> time.strptime(s,"%Y %m-%d %H:%M:%S" )
         
time.struct_time(tm_year=2018, tm_mon=4, tm_mday=11, tm_hour=9, tm_min=10, tm_sec=20, tm_wday=2, tm_yday=101, tm_isdst=-1)
>>> t = time.strptime(s,"%Y %m-%d %H:%M:%S" )
         
>>> time.mktime(t)
         
1523409020.0
时间转换.png

datetime模块

>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2018, 4, 11, 9, 18, 47, 211732)
>>> d = datetime.datetime.now()
>>> dir(d)
['__add__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__radd__', '__reduce__', '__reduce_ex__', '__repr__', '__rsub__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', 'astimezone', 'combine', 'ctime', 'date', 'day', 'dst', 'fold', 'fromordinal', 'fromtimestamp', 'hour', 'isocalendar', 'isoformat', 'isoweekday', 'max', 'microsecond', 'min', 'minute', 'month', 'now', 'replace', 'resolution', 'second', 'strftime', 'strptime', 'time', 'timestamp', 'timetuple', 'timetz', 'today', 'toordinal', 'tzinfo', 'tzname', 'utcfromtimestamp', 'utcnow', 'utcoffset', 'utctimetuple', 'weekday', 'year']
>>> import time
>>> time.time()
1523409561.1562548
>>> datetime.date.fromtimestamp(time.time())
datetime.date(2018, 4, 11)
>>> d.timetuple()
time.struct_time(tm_year=2018, tm_mon=4, tm_mday=11, tm_hour=9, tm_min=19, tm_sec=2, tm_wday=2, tm_yday=101, tm_isdst=-1)
>>> d
datetime.datetime(2018, 4, 11, 9, 19, 2, 528206)
>>> datetime.timedelta(1)
datetime.timedelta(1)
>>> d - datetime.timedelta(1)
datetime.datetime(2018, 4, 10, 9, 19, 2, 528206)
>>> d - datetime.timedelta(days=4)
datetime.datetime(2018, 4, 7, 9, 19, 2, 528206)
>>> d - datetime.timedelta(hours = 3)
datetime.datetime(2018, 4, 11, 6, 19, 2, 528206)
>>> d - datetime.timedelta(minutes = 3)
datetime.datetime(2018, 4, 11, 9, 16, 2, 528206)
>>> d - datetime.timedelta(seconds = 3)
datetime.datetime(2018, 4, 11, 9, 18, 59, 528206)
>>> d =datetime.datetime.now()
>>> d.replace(year=2016, month=8)
datetime.datetime(2016, 8, 11, 9, 23, 19, 76005)

random模块

import random

count = 0
while count < 20:
    print(random.randrange(1, 3))   # 不包括3
    print("------> %d" % random.randint(1, 3))  # 包括3
    count += 1

# 选取0到100之间的随机偶数
print(random.randrange(0, 100, 2))

# 随机浮点数0.0 -> 1.0
print(random.random())

# 给定的字符集中随意选取一个字符
print(random.choice("sdfasdgasdfg!@#$"))

# 从指定的字符集中选取指定数量的字符,return列表
print(random.sample("sdfasdfas", 3))

# 生成16位随机验证码
import string

s = string.ascii_letters + string.punctuation + string.digits
check = "".join(random.sample(s, 16))
print(check)

# 洗牌
a = list(range(0, 10))
random.shuffle(a)
print(a)

os模块

os. getcwd ( )
Return a string representing the current working directory.

os. listdir ( path='.' )
Return a list containing the names of the entries in the directory given by path. The list is in arbitrary order, and does not include the special entries '.' and '..' even if they are present in the directory.

os. remove ( path, *, dir_fd=None )
Remove (delete) the file path. If path is a directory, OSError is raised. Use rmdir() to remove directories.

os. removedirs ( name )
Remove directories recursively. Works like rmdir() except that, if the leaf directory is successfully removed, removedirs() tries to successively remove every parent directory mentioned in path until an error is raised (which is ignored, because it generally means that a parent directory is not empty). For example, os.removedirs('foo/bar/baz') will first remove the directory 'foo/bar/baz', and then remove 'foo/bar' and 'foo' if they are empty. Raises OSError if the leaf directory could not be successfully removed.

os.path.isfile()
os.path.isdir()
os.path.isabs()

os.path.split()  #  os.path.split('/home/swaroop/byte/code/poem.txt') 结果:('/home/swaroop/byte/code', 'poem.txt') 
os.path.splitext() #  os.path.splitext('/usr/local/test.py')    结果:('/usr/local/test', '.py')
os.path.dirname()
os.path.abspath(".")  # 获取绝对路径
os.path.basename("/home/fbo") # 返回fbo
os.system("df -h")  # 运行shell命令,成功返回0
os.getenv("PATH") #  获取系统变量
os.environ  # os.environ['PATH']
os.environ.setdefault('HOME','/home/alex') # 设置零时系统环境变量
os.linesep
os.name
os.makedirs(r“c:\python\test”)
os.mkdir(“test”)
os.stat(file)
os.chmod(file)
os.path.getsize(file)
os.path.join(dir,filename)
os.chdir(dirname)
os.get_terminal_size()
os.kill(10884,signal.SIGKILL)
os.png

sys模块

sys.argv 命令行参数List,第一个元素是程序本身路径
sys.exit(n) 退出程序,正常退出时exit(0)
sys.version 获取Python解释程序的版本信息
sys.maxint 最大的Int值, python3改为maxsize
sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform 返回操作系统平台名称
sys.stdout.write('please:') #标准输出 , 引出进度条的例子, 注,在py3上不行,可以用print代替
val = sys.stdin.readline()[:-1] #标准输入
sys.getrecursionlimit() #获取最大递归层数
sys.setrecursionlimit(1200) #设置最大递归层数
sys.getdefaultencoding() #获取解释器默认编码
sys.getfilesystemencoding #获取内存数据存到文件里的默认编码

shutil 模块

高级的文件,文件夹,压缩包处理模块

import shutil

# 将文件内容拷贝到另一个文件中,目标文件必须存在
shutil.copyfileobj(open("test.html", "r"), open("new.html", 'w'))

# 拷贝文件,目标文件可以不存在
shutil.copyfile("test.html", "new_test.html")

# 仅拷贝权限。内容、组、用户均不变
import os
os.system("touch abc")
print(os.system("ls -l abc"))
shutil.copymode("/home/fbo", "abc")
print(os.system("ls -l abc"))

# 仅拷贝状态的信息,包括:mode bits, atime, mtime, flags
os.system("ls -l /tmp/vmware-fbo/vmware-apploader-3565.log")
os.system("touch cs")
os.system("ls -l cs")
shutil.copystat("/tmp/vmware-fbo/vmware-apploader-3565.log", "cs")
os.system("ls -l cs")

shutil.copy(src, dst) # 拷贝文件和权限
shutil.copy2(src,dst) # 拷贝文件和状态信息

# 递归的去拷贝文件夹目标目录不能存在,注意对folder2目录父级目录要有可写权限,ignore的意思是排除
shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))

# 递归的去删除文件
shutil.rmtree('folder1')

# 递归的去移动文件,它类似mv命令,其实就是重命名。
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 两个模块来进行的,详细:

  • zipfile压缩&解压缩
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()
  • tarfile压缩&解压缩
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()

序列化模块(json && pickle)

什么是序列化
序列化是指把内存离色数据类型转变成字符串,是其能够存储到硬盘或通过网络传输到远程,因为硬盘或网络传输时只支持bytes类型.

用于序列化有两个模块:

  • json, 用于字符串和python数据类型之间的转换
  • pickle, 用于python特有的类型和python的数据类型间进行转换
import pickle
data = {'k1': 123, 'k2': 'Hello'}
# pickle.dumps 将数据通过特殊的形式转换为只有python语言认识的字符串
p_str = pickle.dumps(data)
print(p_str)

# pickle.dump 将数据通过特殊的形式转化为只有python语言认识的字符串,并写入文件
with open('result.pk', 'wb') as fp:
    pickle.dump(data, fp)

import json

j_str = json.dumps(data)
print(j_str)
with open("result.json", "wb") as fj:
    pickle.dump(data, fj)

shelve模块

shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式
序列化

import shelve

f = shelve.open('shelve_test')

names = ['alex', 'rain', 'test']
info = {'name': 'alex', 'age': 22}

f["names"] = names
f["info"] = info

f.close()

反序列化

d = shelve.open("shelve_test")

print(d["names"])
print(d["info"])

print(type(d["names"])) # list类型

xml模块

xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但是json使用起来更简单.
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协议在各个语言里的都是支持的,python中可以用以下模块操作xml

import xml.etree.ElementTree as ET

tree = ET.parse("xmltest.xml")
root = tree.getroot()
print(root.tag)

# 遍历xml文档
for child in root:
    print(child.tag, child.attrib)
    for i in child:
        print(i.tag, i.text)

# 只遍历year节点
for node in root.iter('year'):
    print(node.tag, node.text)

修改和删除xml文件内容

import xml.etree.ElementTree as ET

tree = ET.parse("xmltest.xml")
root = tree.getroot()

# 修改
for node in root.iter("year"):
    new_year = int(node.text) + 1
    node.text = str(new_year)
    node.set("update", "yes")

tree.write("xmltest.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(name, "age")
age.text = '19'

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

# 打印生成的对象
ET.dump(et)

configparser模块

该模块用于配置和修改常见配置文件,如:

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

[bitbucket.org]
User = hg

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

修改配置文件


import configparser

config = configparser.ConfigParser()
print(config.sections())

config.read('example.ini')
print(config.sections())

print('bitbucket.org' in config.sections())
print("bytebong.com" in config.sections())

print(config['bitbucket.org']['User'])
print(config['DEFAULT']['Compression'])

topsecret = config['topsecret.server.com']
print(topsecret['ForwardX11'])
print(topsecret['Port'])

for key in config['bitbucket.org']:
    print(key)

print(config['bitbucket.org']['ForwardX11'])

其他修改方法:

[group1]
k1 = v1
k2:v2

[group2]
k1 = v1

import ConfigParser

config = ConfigParser.ConfigParser()
config.read('i.cfg')

# ########## 读 ##########
#secs = config.sections()
#print secs
#options = config.options('group2')
#print options

#item_list = config.items('group2')
#print item_list

#val = config.get('group1','key')
#val = config.getint('group1','key')

# ########## 改写 ##########
#sec = config.remove_section('group1')
#config.write(open('i.cfg', "w"))

#sec = config.has_section('wupeiqi')
#sec = config.add_section('wupeiqi')
#config.write(open('i.cfg', "w"))


#config.set('group2','k1',11111)
#config.write(open('i.cfg', "w"))

#config.remove_option('group2','age')
#config.write(open('i.cfg', "w"))

hashlib模块

加密算法介绍

  • HASH
    Hash,一般翻译做“散列”,也有直接音译为”哈希”的,就是把任意长度的输入(又叫做预映射,pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。
    简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
    HASH主要用于信息安全领域中加密算法,他把一些不同长度的信息转化成杂乱的128位的编码里,叫做HASH值.也可以说,hash就是找到一种数据内容和数据存放地址之间的映射关系
  • MD5
    • 什么是MD5算法
      MD5讯息摘要演算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码杂凑函数,可以产生出一个128位的散列值(hash value),用于确保信息传输完整一致。MD5的前身有MD2、MD3和MD4。
    • MD5功能
      输入任意长度的信息,经过处理,输出为128位的信息(数字指纹);
      不同的输入得到的不同的结果(唯一性);
    • MD5算法是否可逆?
      MD5不可逆的原因是其是一种散列函数,使用的是hash算法,在计算过程中原文的部分信息是丢失了的。

MD5用途

  1. 防止被篡改
  2. 防止直接看到明文
  3. 防止抵赖(数字签名)

SHA-1
安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。
SHA是美国国家安全局设计的,由美国国家标准和技术研究院发布的一系列密码散列函数。
由于MD5和SHA-1于2005年被山东大学的教授王小云破解了,科学家们又推出了SHA224, SHA256, SHA384, SHA512,当然位数越长,破解难度越大,但同时生成加密的消息摘要所耗时间也更长。目前最流行的是加密算法是SHA-256 .
MD5与SHA-1的比较
由于MD5与SHA-1均是从MD4发展而来,它们的结构和强度等特性有很多相似之处,SHA-1与MD5的最大区别在于其摘要比MD5摘要长32 比特。对于强行攻击,产生任何一个报文使之摘要等于给定报文摘要的难度:MD5是2128数量级的操作,SHA-1是2160数量级的操作。产生具有相同摘要的两个报文的难度:MD5是264是数量级的操作,SHA-1 是280数量级的操作。因而,SHA-1对强行攻击的强度更大。但由于SHA-1的循环步骤比MD5多80:64且要处理的缓存大160比特:128比特,SHA-1的运行速度比MD5慢。

Python的 提供的相关模块

用于加密相关的操作,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法

import hashlib

m = hashlib.md5()
m.update(b"hello")
m.update(b"It's me")
print(m.digest())
m.update(b"It's been a long time since last time we ...")

# 二进制hash
print(m.digest())
# 十六进制hash
print(len(m.hexdigest()))
print(m.hexdigest())

hash = hashlib.md5()
hash.update('admin'.encode('utf-8'))
print(hash.hexdigest())

hash = hashlib.sha1()
hash.update('admin'.encode('utf-8'))
print(hash.hexdigest())

hash = hashlib.sha256()
hash.update('admin'.encode('utf-8'))
print(hash.hexdigest())

hash = hashlib.sha384()
hash.update('admin'.encode('utf-8'))
print(hash.hexdigest())

hash = hashlib.sha512()
hash.update('admin'.encode('utf-8'))
print(hash.hexdigest())

subprocess模块

The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace several older modules and functions:

  • os.system
  • os.spawn*

The recommended approach to invoking subprocesses is to use the run() function for all use cases it can handle. For more advanced use cases, the underlying Popen interface can be used directly.
The run() function was added in Python 3.5; if you need to retain compatibility with older versions, see the Older high-level API section.

三种执行命令的方法

  • subprocess.run(*popenargs, input=None, timeout=None, check=False, **kwargs) #官方推荐
  • subprocess.call(*popenargs, timeout=None, **kwargs) #跟上面实现的内容差不多,另一种写法
  • subprocess.Popen() #上面各种方法的底层封装

run()方法
Run command with arguments and return a CompletedProcess instance.The returned instance will have attributes args, returncode, stdout and stderr. By default, stdout and stderr are not captured, and those attributes will be None. Pass stdout=PIPE and/or stderr=PIPE in order to capture them.
If check is True and the exit code was non-zero, it raises a CalledProcessError. The CalledProcessError object will have the return code in the returncode attribute, and output & stderr attributes if those streams were captured.
If timeout is given, and the process takes too long, a TimeoutExpired exception will be raised.
The other arguments are the same as for the Popen constructor.

subprocess.run(['df','-h'],stderr=subprocess.PIPE,stdout=subprocess.PIPE,check=True)
subprocess.run('df -h|grep disk1',shell=True) #shell=True的意思是这条命令直接交给系统去执行,不需要python负责解析

call()方法

#执行命令,返回命令执行状态 , 0 or 非0
>>> retcode = subprocess.call(["ls", "-l"])

#执行命令,如果命令结果为0,就正常返回,否则抛异常
>>> subprocess.check_call(["ls", "-l"])
0

#接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果 
>>> subprocess.getstatusoutput('ls /bin/ls')
(0, '/bin/ls')

#接收字符串格式命令,并返回结果
>>> subprocess.getoutput('ls /bin/ls')
'/bin/ls'

#执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res
>>> res=subprocess.check_output(['ls','-l'])
>>> res
b'total 0\ndrwxr-xr-x 12 alex staff 408 Nov 2 11:05 OldBoyCRM\n'

Popen()方法
常用参数:

  • args:shell命令,可以是字符串或者序列类型(如:list,元组)
  • stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
  • preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
  • shell:同上
  • cwd:用于设置子进程的当前目录
  • env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。

Popen会在发起命令后立刻返回,而不等命令执行结果。这样的好处是什么呢?
如果你调用的命令或脚本 需要执行10分钟,你的主程序不需卡在这里等10分钟,可以继续往下走,干别的事情,每过一会,通过一个什么方法来检测一下命令是否执行完成就好了。
Popen调用后会返回一个对象,可以通过这个对象拿到命令执行结果或状态等,该对象有以下方法:

  • poll()
    Check if child process has terminated. Returns returncode
  • wait()
    Wait for child process to terminate. Returns returncode attribute.
  • terminate()终止所启动的进程Terminate the process with SIGTERM
  • kill() 杀死所启动的进程 Kill the process with SIGKILL
  • communicate()与启动的进程交互,发送数据到stdin,并从stdout接收输出,然后等待任务结束
>>> a = subprocess.Popen('python3 guess_age.py',stdout=subprocess.PIPE,stderr=subprocess.PIPE,stdin=subprocess.PIPE,shell=True)

>>> a.communicate(b'22')

(b'your guess:try bigger\n', b'')
  • send_signal(signal.xxx)发送系统信号
  • pid 拿到所启动进程的进程号

logging模块

python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为 debug(), info(), warning(), error() and critical()5个级别.

import logging

logging.warning("user [alex] attempted wrong password more than 3 times")
logging.critical("server is down")
logging日志级别.png

将日志写入文件:

import logging

logging.basicConfig(filename="example.log", level=logging.INFO)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')

其中下面这句中的level=loggin.INFO意思是,把日志纪录级别设置为INFO,也就是说,只有比日志是INFO或比INFO级别更高的日志才会被纪录到文件里,在这个例子, 第一条日志是不会被纪录的,如果希望纪录debug的日志,那把日志级别改成DEBUG就行了。
logging.basicConfig(filename='example.log',level=logging.INFO)

自定义日志格式

import logging

logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%y %I:%M:%S %p')
logging.warning('is when this event was logged.')
format格式.png

日志同时输出到屏幕和文件
如果想同时把log打印在屏幕和文件日志里,就需要了解一点复杂的知识 了:
Python 使用logging模块记录日志涉及四个主要类,使用官方文档中的概括最为合适:

  • logger提供了应用程序可以直接使用的接口;
  • handler将(logger创建的)日志记录发送到合适的目的输出;
  • filter提供了细度设备来决定输出哪条日志记录;
  • formatter决定日志记录的最终输出格式。


    image.png

    一个同时输出到屏幕、文件、带filter的完成例子:

import logging

class IgnoreBackupFilter(logging.Filter):
    """忽略带db backup 日志"""
    def filter(self, record):
        return "db backup" not in record.getMessage()

# console handler
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
# file handler
fh = logging.FileHandler('mysql.log')
fh.setLevel(logging.WARNING)

# formatter
formatter =logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# bind formatter to ch
ch.setFormatter(formatter)
ch.setFormatter(formatter)

logger = logging.getLogger("Mysql")
logger.setLevel(logging.DEBUG)

# add handler to logger instance
logger.addHandler(ch)
logger.addHandler(fh)

# add filter
logger.addFilter(IgnoreBackupFilter())

logger.debug('test ...')
logger.info('test info ...')
logger.warning("start to run db backup job ...")
logger.error("test error ...")

文件自动截断例子

import logging

from logging import handlers

logger = logging.getLogger(__name__)

log_file = "timelog.log"

# fh = handlers.RotatingFileHandler(filename=log_file, maxBytes=10, backupCount=3)
fh = handlers.TimedRotatingFileHandler(filename=log_file, when="S", interval=5, backupCount=3)

formatter = logging.Formatter('%(asctime)s %(module)s:%(lineno)d %(message)s')

fh.setFormatter(formatter)
logger.addHandler(fh)

logger.warning("test1")
logger.warning("test2")
logger.warning("test3")
logger.warning("test4")
logger.warning("test5")

re模块

正则表达式就是字符串的匹配规则,在多数编程语言里都有相应的支持,python里对应的模块是re

常用的表达式规则

'.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
'^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE)
'$' 匹配字符结尾, 若指定flags MULTILINE ,re.search('foo.$','foo1\nfoo2\n',re.MULTILINE).group() 会匹配到foo1
'' 匹配号前的字符0次或多次, re.search('a*','aaaabac') 结果'aaaa'
'+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
'?' 匹配前一个字符1次或0次 ,re.search('b?','alex').group() 匹配b 0次
'{m}' 匹配前一个字符m次 ,re.search('b{3}','alexbbbs').group() 匹配到'bbb'
'{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb']
'|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
'(...)' 分组匹配, re.search("(abc){2}a(123|45)", "abcabca456c").group() 结果为'abcabca45'
'\A' 只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的,相当于re.match('abc',"alexabc") 或^
'\Z' 匹配字符结尾,同$
'\d' 匹配数字0-9
'\D' 匹配非数字
'\w' 匹配[A-Za-z0-9]
'\W' 匹配非[A-Za-z0-9]
's' 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t'
'(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'}

re的匹配语法有以下几种

  • re.match从头开始匹配
  • re.search匹配包含
  • re.findall把所有匹配的字符放到以列表中的元素返回
  • re.split以匹配的字符当做列表分割符
  • re.sub匹配字符并替换
  • re.fullmatch全部匹配
import re

obj = re.match("\d+", "1234567890sdfasdf")
for i in obj.group():
    print(i)

print(obj.group())

a = re.compile(r"""\d + # the integral part
                \. # the decimal point
                \d * # some fractional digits""",
                re.X)

b = re.compile(r"\d+\.\d*")

rt = re.search('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', "1.2.3.4asdfadf")
print(rt)

rt1 = re.findall('\d+', '1234sdfasdf123153')
print(rt1)

rt2 = re.sub("[a-z]+", "sb", "asdfasd 1234123512!@#$%")
print(rt2)

s = '9-2*5/3+7/3*99/4*2998+10*568/14'
rt3 = re.split('[\*\-\/\+]', s)
print(rt3)
rt4 = re.split('[\*\-\/\+]', s, 3)
print(rt4)

rt5 = re.fullmatch('\w+@\w+\.(com|cn|edu)',"alex@oldboyedu.cn")
print(rt5)

练习题

  1. logging模块有几个日志级别?

logging模块有5个日志级别: DEBUG, INFO, WARNING,ERROR,CRITICAL

  1. 请配置logging模块,使其在屏幕和文件里同时打印以下格式的日志

2017-10-18 15:56:26,613 - access - ERROR - account [1234] too many login attempts

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午1:09
# @File : ex_002.py

import logging

logger = logging.getLogger("log")
logger.setLevel(logging.DEBUG)

fh = logging.FileHandler("ex002.log")
ch = logging.StreamHandler()

# 2017-10-18 15:56:26,613 - access - ERROR - account [1234] too many login attempts

formatter = logging.Formatter("%(asctime)s - access - %(levelname)s - %(message)s")

fh.setFormatter(formatter)
ch.setFormatter(formatter)

logger.addHandler(ch)
logger.addHandler(fh)

logger.error("account [1234] too many login attempts")
  1. json、pickle、shelve三个区别是什么?
    json用于字符串和python类型之间的转化,只适用于int,str,list,字典,元祖等简单对象,可以和其他编程语言交换内存信息;pickle是python专用的,支持将python所有对象转化为pickle类型存储;shelve是通过简单kv模式将持久化存储python对象的模块;
  2. json的作用是什么?
    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,易于阅读和编写,同时也易于机器解析和生成。
  3. subprocess执行命令方法有几种?subprocess执行命令方法有几种?
    subprocess执行命令的方法有run(), call(), Popen()
  4. 为什么要设计好目录结构?
    有两个目的: 1. 可读性高 2. 可维护性高
  5. 打印出命令行的第一个参数。例如:

python argument.py luffy
打印出 luffy

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午6:07
# @File : argument.py

import sys

print(sys.argv[1])
  1. 代码如下:

'''
Linux当前目录/usr/local/nginx/html/
文件名:index.html
'''
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(index.html)))
print(BASE_DIR)

  • 打印的内容是设么?
  • os.path.dirname和os.path.abspath是什么含义?
打印的是 /usr/local/nginx/html/
os.path.dirname 是获取路径的文件夹名
os.path.abspath 获取文件的绝对路径
  1. 通过configparser模块完成以下功能:
    配置文件my.cnf

[DEFAULT]
[client]
port = 3306
socket = /data/mysql_3306/mysql.sock
[mysqld]
explicit_defaults_for_timestamp
port = 3306
socket = /data/mysql_3306/mysql.sock
back_log = 80
basedir = /usr/local/mysql
tmpdir = /tmp
datadir = /data/mysql_3306
default-time-zone = '+8:00'

  • 修改时区default-time='+8:00'为校准的全球时间'+00:00'
  • 删除explicit_defaults_for_timestamp
  • 为DEFAULT增加一条character-set-server = utf8
#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午6:22
# @File : ex009.py

import configparser

# 允许配置文件里key没有value
config = configparser.ConfigParser(allow_no_value=True)

config.read("my.cnf")

config.set("mysqld", "default-time-zone", "+00:00")
config.remove_option('mysqld', 'explicit_defaults_for_timestamp')
config.set("DEFAULT", "character-set-server", "utf8")

# print(config['mysqld']["default-time-zone"])

config.write(open('my.cnf', 'w'))
  1. 写一个6位随机验证码程序(使用random模块),要求验证码中至少包含一个数字、一个小写字母、一个大写字母.
#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午6:46
# @File : ex010.py.py

import random, string

s = string.digits + string.ascii_lowercase + string.ascii_uppercase + string.punctuation

check_code = ''.join(random.sample(s, 6))

print(check_code)
  1. 利用正则表达式提取到 luffycity.com ,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>luffycity.com</title>
</head>
<body>
</body>
</html>
#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午6:51
# @File : ex011.py

import re

with open("ex011.html", 'r') as f:
    data = f.read()
    print(data)
    rt = re.search("luffycity.com", data)
    print(rt.group())
  1. 写一个用户登录验证程序,文件如下:
    1234.json

{"expire_date": "2021-01-01", "id": 1234, "status": 0, "pay_day": 22, "password": "abc"}

  • 用户名为json文件名,密码为 password。
  • 判断是否过期,与expire_date进行对比。
  • 登陆成功后,打印“登陆成功”,三次登陆失败,status值改为1,并且锁定账号。
#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午7:00
# @File : ex012.py.py

import json, os, time


def main():
    def login():
        count = 0
        temp = ''
        flag = 0
        while count < 3:
            user = input("Input your account: ")
            password = input("Input your password: ")
            filename = user + ".json"
            if os.path.isfile(filename):
                data = json.load(open(filename))
                print(data)
                if password == data['password']:
                    if data['status'] == 0:
                        expire_date = time.mktime(time.strptime(data["expire_date"], "%Y-%m-%d"))
                        now = time.time()
                        if now > expire_date:
                            print("Your account has expired!!!")
                        else:
                            print("Login!!!")
                            count = 3
                    else:
                        print("Your account has been locked, plz contact administrator.")
                        count = 3
                else:
                    if count == 1 and temp == user:
                        flag = 1
                    if count == 2 and flag == 1 and temp == user:
                        data['status'] = 1
                    temp = user
                    json.dump(data, open(filename, 'w'))
                    print("Password wrong!")
            else:
                print("User not exist!")
            count += 1
        else:
            print("See you next time!")

    login()


if __name__ == "__main__":
    main()
  1. 把第12题三次验证的密码进行hashlib加密处理。即:json文件保存为md5的值,然后用md5的值进行验证。
#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午7:00
# @File : ex013.py

import json, os, time, hashlib


def main():
    def login():
        count = 0
        temp = ''
        flag = 0
        while count < 3:
            user = input("Input your account: ")
            password = input("Input your password: ")
            hash = hashlib.md5()
            hash.update(password.encode('utf-8'))
            password = hash.hexdigest()
            # print(password)
            filename = user + ".json"
            if os.path.isfile(filename):
                data = json.load(open(filename))
                # print(data)
                if password == data['password']:
                    if data['status'] == 0:
                        expire_date = time.mktime(time.strptime(data["expire_date"], "%Y-%m-%d"))
                        now = time.time()
                        if now > expire_date:
                            print("Your account has expired!!!")
                        else:
                            print("Login!!!")
                            count = 3
                    else:
                        print("Your account has been locked, plz contact administrator.")
                        count = 3
                else:
                    if count == 1 and temp == user:
                        flag = 1
                    if count == 2 and flag == 1 and temp == user:
                        data['status'] = 1
                    temp = user
                    json.dump(data, open(filename, 'w'))
                    print("Password wrong!")
            else:
                print("User not exist!")
            count += 1
        else:
            print("See you next time!")

    login()


if __name__ == "__main__":
    main()
  1. 最近fbo买了个tesla,通过转账的形式,并且支付了5%的手续费,tesla价格为75万。文件为json,请用程序实现该转账行为。
    需求如下:
  • 目录结构:

.
├── account
│ ├── fbo.json
│ └── tesla.json
└── bin
| └── start.py

  • 当执行start.py时,出现交互窗口

------- FBO Bank ---------

  1. 账户信息
  2. 转账fbo
    选择1 账户信息 显示fbo的当前账户余额
    选择2 转账 直接扣掉75万和利息费用并且tesla账户增加75万
#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午9:03
# @File : start.py

import os, json

BASE_DIR = os.path.abspath("..")

ACCOUNT_DIR = os.path.join(BASE_DIR, 'account')

def main():
    fbo = json.load(open(os.path.join(ACCOUNT_DIR, "fbo.json")))
    tesla = json.load(open(os.path.join(ACCOUNT_DIR, "tesla.json")))
    while True:
        print("FBO BANK".center(80, "-"))
        choice = input("1. 账户信息\n2. 转账\n>>>").strip()
        if choice == '1':
            print("Username: %s ---> Bill: %.2fW" %(fbo["name"], fbo["bill"]))
        elif choice == "2":
            fee = tesla["price"] * (1 + tesla["fee"])
            if fee <= fbo["bill"]:
                fbo["bill"] -= fee
                tesla["bill"] += 75
                json.dump(fbo, open(os.path.join(ACCOUNT_DIR, "fbo.json"), "w"))
                json.dump(tesla, open(os.path.join(ACCOUNT_DIR, "tesla.json"), "w"))
        elif choice == "q":
            break
        else:
            print("请输入正确选项!")




if __name__ == "__main__":
    main()
  1. 对上题增加一个需求:提现。
    目录结构如下

.
├── account
│ └── luffy.json
├── bin
│ └── start.py
└── core
└── withdraw.py

  • 当执行start.py时,出现交互窗口

------- FBO Bank ---------

  1. 账户信息
  2. 提现
#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午9:03
# @File : start.py

import os,json,sys

BASE_DIR = os.path.abspath("..")

ACCOUNT_DIR = os.path.join(BASE_DIR, 'account')

sys.path.append(BASE_DIR)
from core.withdraw import withdraw

def main():
    fbo = json.load(open(os.path.join(ACCOUNT_DIR, "fbo.json")))
    tesla = json.load(open(os.path.join(ACCOUNT_DIR, "tesla.json")))
    while True:
        print("FBO BANK".center(80, "-"))
        choice = input("1. 账户信息\n2. 转账\n3. 提现\n>>>").strip()
        if choice == '1':
            print("Username: %s ---> Bill: %.2fW" %(fbo["name"], fbo["bill"]))
        elif choice == "2":
            fee = tesla["price"] * (1 + tesla["fee"])
            if fee <= fbo["bill"]:
                fbo["bill"] -= fee
                tesla["bill"] += 75
                json.dump(fbo, open(os.path.join(ACCOUNT_DIR, "fbo.json"), "w"))
                json.dump(tesla, open(os.path.join(ACCOUNT_DIR, "tesla.json"), "w"))
        elif choice == "3":
            withdraw(fbo)
            json.dump(fbo, open(os.path.join(ACCOUNT_DIR, "fbo.json"), "w"))
        elif choice == "q":
            break
        else:
            print("请输入正确选项!")




if __name__ == "__main__":
    main()

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午9:59
# @File : withdraw.py

def withdraw(user):
    flag = 1
    while flag:
        user_want = input("输入你想提现的金额").strip()
        if user_want.isdigit():
            user_want = float(user_want)
            if user["credit_line"] >= user_want:
                user["credit_line"] -= user_want
                flag = 0
            else:
                print("信用额度不够")
        else:
            print("输入错误")
    return user
  1. 尝试把上一章的验证用户登陆的装饰器添加到提现和转账的功能上。
#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午9:03
# @File : start.py

import os,json,sys

BASE_DIR = os.path.abspath("..")

ACCOUNT_DIR = os.path.join(BASE_DIR, 'account')

sys.path.append(BASE_DIR)
from core.withdraw import withdraw

def login(func):
    def wapper(*args, **kwargs):
        fbo = json.load(open(os.path.join(ACCOUNT_DIR, "fbo.json")))
        count = 0
        while count < 3:
            name = input("Username: ")
            password = input("Password: ")
            filename = os.path.join(ACCOUNT_DIR, name + ".json")
            if os.path.isfile(filename):
                user = json.load(open(filename))
                if user["name"] == name and user["password"] == password:
                    print(name.center(80, "+"))
                    func(*args, **kwargs)
                    count = 3
                else:
                    print("密码错误")
            else:
                print("用户不存在")
            count += 1
        else:
            print("再见")
    return wapper


@login
def main():
    fbo = json.load(open(os.path.join(ACCOUNT_DIR, "fbo.json")))
    tesla = json.load(open(os.path.join(ACCOUNT_DIR, "tesla.json")))
    while True:
        print("FBO BANK".center(80, "-"))
        choice = input("1. 账户信息\n2. 转账\n3. 提现\n>>>").strip()
        if choice == '1':
            print("Username: %s ---> Bill: %.2fW" %(fbo["name"], fbo["bill"]))
        elif choice == "2":
            fee = tesla["price"] * (1 + tesla["fee"])
            if fee <= fbo["bill"]:
                fbo["bill"] -= fee
                tesla["bill"] += 75
                json.dump(fbo, open(os.path.join(ACCOUNT_DIR, "fbo.json"), "w"))
                json.dump(tesla, open(os.path.join(ACCOUNT_DIR, "tesla.json"), "w"))
        elif choice == "3":
            withdraw(fbo)
            json.dump(fbo, open(os.path.join(ACCOUNT_DIR, "fbo.json"), "w"))
        elif choice == "q":
            break
        else:
            print("请输入正确选项!")




if __name__ == "__main__":
    main()
  1. 对第15题的用户转账、登录、提现操作均通过logging模块记录日志,日志文件位置如下:

.
├── account
│ └── luffy.json
├── bin
│ └── start.py
└── core
| └── withdraw.py
└── logs
└── bank.log

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午9:03
# @File : start.py

import os,json,sys,logging

BASE_DIR = os.path.abspath("..")
ACCOUNT_DIR = os.path.join(BASE_DIR, 'account')
LOG_DIR = os.path.join(BASE_DIR, "logs")

logger = logging.getLogger("bank.log")
formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(funcName)s - %(message)s")
log_file = os.path.join(LOG_DIR, "bank.log")
hf = logging.FileHandler(log_file)
hc = logging.StreamHandler()
hf.setFormatter(formatter)
hc.setFormatter(formatter)
logger.addHandler(hf)
logger.addHandler(hc)

sys.path.append(BASE_DIR)
from core.withdraw import withdraw

def login(func):
    def wapper(*args, **kwargs):
        fbo = json.load(open(os.path.join(ACCOUNT_DIR, "fbo.json")))
        count = 0
        while count < 3:
            name = input("Username: ")
            password = input("Password: ")
            filename = os.path.join(ACCOUNT_DIR, name + ".json")
            if os.path.isfile(filename):
                user = json.load(open(filename))
                if user["name"] == name and user["password"] == password:
                    print(name.center(80, "+"))
                    func(*args, **kwargs)
                    count = 3
                else:
                    message = "密码错误"
                    logger.error(message)
            else:
                message = "用户不存在"
                logger.error(message)
            count += 1
        else:
            message = "再见"
            logger.info(message)
    return wapper


@login
def main():
    fbo = json.load(open(os.path.join(ACCOUNT_DIR, "fbo.json")))
    tesla = json.load(open(os.path.join(ACCOUNT_DIR, "tesla.json")))
    while True:
        print("FBO BANK".center(80, "-"))
        choice = input("1. 账户信息\n2. 转账\n3. 提现\n>>>").strip()
        if choice == '1':
            print("Username: %s ---> Bill: %.2fW" %(fbo["name"], fbo["bill"]))
            message = "%s 查帐成功" % fbo["name"]
            logger.info(message)
        elif choice == "2":
            fee = tesla["price"] * (1 + tesla["fee"])
            if fee <= fbo["bill"]:
                fbo["bill"] -= fee
                tesla["bill"] += 75
                json.dump(fbo, open(os.path.join(ACCOUNT_DIR, "fbo.json"), "w"))
                json.dump(tesla, open(os.path.join(ACCOUNT_DIR, "tesla.json"), "w"))
                message = "%s 转账tesla成功" % fbo['name']
                logger.info(message)
        elif choice == "3":
            withdraw(fbo, logger)
            json.dump(fbo, open(os.path.join(ACCOUNT_DIR, "fbo.json"), "w"))
        elif choice == "q":
            break
        else:
            message = "请输入正确选项!"
            logger.error(message)




if __name__ == "__main__":
    main()

#!/usr/bin/env python3
# Author    : fbo
# @Time : 18-4-14 下午9:59
# @File : withdraw.py

def withdraw(user, logger):
    flag = 1
    while flag:
        user_want = input("输入你想提现的金额").strip()
        if user_want.isdigit():
            user_want = float(user_want)
            if user["credit_line"] >= user_want:
                user["credit_line"] -= user_want
                flag = 0
            else:
                message = "信用额度不够"
                logger.error(message)
        else:
            message = "输入错误"
            logger.error(message)
    return user
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容