python运维开发(五)----模块、生成器

内容目录

  • 双层装饰器
  • 字符串格式化
  • 生成器和迭代器
  • 递归
  • 模块

双层装饰器

需求场景介绍:

现有用户登录系统,普通用户能查看自己相关信息的权限,管理员用户能查看所有用户的权限,可以做两个装饰器来实现需求,一个来限制登录,另一个为双层判断是否为管理员账号,可以这样来实现

USER_INFO = {}
def check_login(func):
    def inner(*args, **kwargs):
        if USER_INFO.get('is_login', None):
            ret = func(*args, **kwargs)
            return ret
        else:
            print('请登录')
    return inner
def check_admin(func):
    def inner(*args, **kwargs):
        if USER_INFO.get('is_login', None):
            if USER_INFO.get('user_type', None) == 2:
                func(*args, **kwargs)
            else:
                print('无权限')
        else:
            print('请登录')
    return inner
@check_admin
def index():
    """
    管理员的功能
    :return:
    """
    print('Index')

@check_login
def home():
    '''
    普通用户功能
    :return:
    '''
    print('home')
def main():
    while True:
        print('''
            1 登陆;2 查看用户信息; 3 管理员信息;
            ''')
        inputinfo = input('选择操作:>>')
        if inputinfo == '1':
            user = input('输入用户名:')
            if user == 'ljb':
                print('%s登陆成功'%user)
                USER_INFO['is_login'] = True
                USER_INFO['user_type'] = 1
            if user == 'jabe':
                print('%s管理员登陆成功' % user)
                USER_INFO['is_login'] = True
                USER_INFO['user_type'] = 2
        if inputinfo == '2':
            home()
        if inputinfo == '3':
            index()
main()

  此种方法有很大的局限性,如果该需求为多级管理的话,那就需要创建很多功能类似的装饰器函数,权限中嵌套级别,还有一种方法就不用创建那么多的重复功能的装饰器,只需创建新增的功能装饰器即可,调用时候采用多层嵌套方式,比如上面的需求我们也可以这样实现

USER_INFO = {}
# USER_INFO['is_login'] = True
# USER_INFO['user_type'] = 2

def check_login(func):
    def inner(*args, **kwargs):
        if USER_INFO.get('is_login', None):
            ret = func(*args, **kwargs)
            return ret
        else:
            print('请登录')
    return inner

def check_admin(func):
    def inner():
        if USER_INFO.get('user_type', None) == 2:
            ret = func()
            return ret
        else:
            print('无权限查看')

    return inner

@check_login
@check_admin
def index():
    """
    管理员的功能
    :return:
    """
    print('Index')

@check_login
def home():
    '''
    普通用户功能
    :return:
    '''
    print('home')
def main():
    while True:
        print('''
            1 登陆;2 查看用户信息; 3 管理员信息;
            ''')
        inputinfo = input('选择操作:>>')
        if inputinfo == '1':
            user = input('输入用户名:')
            if user == 'ljb':
                print('%s登陆成功'%user)
                USER_INFO['is_login'] = True
                USER_INFO['user_type'] = 1
            if user == 'jabe':
                print('%s管理员登陆成功' % user)
                USER_INFO['is_login'] = True
                USER_INFO['user_type'] = 2
        if inputinfo == '2':
            home()
        if inputinfo == '3':
            index()
main()

  

字符串的格式化输出

python字符串两种输出方式,目前两种方式均并存:

  • 百分号方式:方式比较老,功能上如format方式全
  • format方式:比较新的方式,比百分号方式显著的多了几个功能(二进制方式、居中对齐、%小数取百分号形式、填充等)

1 百分号方式

   使用语法:%[(name)][flags][witdth].[preceision]typecode

  name:可选参数用于选择指定的key

  flags:可选,可供选择的值

  • +      右对齐,正数前加正号,负数前加负号
  • -       左对齐,正数前无符号,负数前加负号
  • 空格  右对齐,正数前加空格,负数前加负号
  • 0      右对齐,正数前无符号,负数前加负号,用0填充空白处
  • witdth:可选
  • .preceision 可选,小数点后保留位数
  • typecode必选参数
    • s 获取传入对象的__str__方法的返回值,并将其格式化到指定位置
    • r 获取传入对象的__repr__方法的返回值,并将其格式化到指定位置
    • c 整数:将数字转换成其unicode对应的值,10进制范围为 0 <= i <= 1114111(py27则只支持0-255);字符:将字符添加到指定位置
    • o 将整数转换成 八  进制表示,并将其格式化到指定位置
    • x 将整数转换成十六进制表示,并将其格式化到指定位置
    • d 将整数、浮点数转换成 十 进制表示,并将其格式化到指定位置
    • e 将整数、浮点数转换成科学计数法,并将其格式化到指定位置(小写e)
    • E 将整数、浮点数转换成科学计数法,并将其格式化到指定位置(大写E)
    • f 将整数、浮点数转换成浮点数表示,并将其格式化到指定位置(默认保留小数点后6位)
    • g 自动调整将整数、浮点数转换成 浮点型或科学计数法表示(超过6位数用科学计数法),并将其格式化到指定位置(如果是科学计数则是e;)
    • G 自动调整将整数、浮点数转换成 浮点型或科学计数法表示(超过6位数用科学计数法),并将其格式化到指定位置(如果是科学计数则是E;)
    • % 当字符串中存在格式化标志时,需要用 %%表示一个百分号

 

#10表示占位符占用宽度为10个字符

print('====%+10d====,====%+10d===='%(12,-23)) #+右对齐,正数前加+负数前加-

print('====%-10d====,====%-10d===='%(12,-23)) #-左对齐,正数前无符号,负数前加-

print('====% 10d====,====% 10d===='%(12,-23)) #空格右对齐,正数前无符号,负数前加-

print('====%010d====,====%010d===='%(12,-23)) #0右对齐,空白用0补充,正数前无符号,负数前加-

print('====%.2f===='%(0.23421234)) #表示保留2位小数

#c为数字的话打印对应unicode的值为字母,如果为字符的话则打印对应的字符;

#o表示转换为8进制显示;x表示转换为16进制表示;

#g表示将数字自动调整为计数法表示,大于6位计数法表示,小于等于6位直接显示

print("sddf %c === %o === %x == %g ===== %g ====%c" %(65, 15, 15, 100000,1000000,'j')) 

print('i am %(pp).2f %% '% {'pp':123.34532352 })  ##字符串中包含一个%号时需要 用%%

#输出
====       +12====,====       -23====

====12        ====,====-23       ====

====        12====,====       -23====

====0000000012====,====-000000023====

====0.23==== 

sddf A === 17 === f == 100000===== 1e+06 ====j

i am 123.35 % 

  百分号方式几种常见用法

s1 = 'i am %s' % 'jabe'  #普通方式

s2 = 'i am %s age %d' % ('jabe', 23) #s和d的用法

s3 = 'i am %(name)s age %(age)d' % {'name': 'alex', 'age': 18} #字典方式引用

s4 = 'percent %.2f' % 99.97623 #f浮点数,保留2位小数

s5 = 'i am %(pp).2f' % {'pp':123.425556, } #通过名字来指定值,字典方式

s6 = 'i am %(pp).2f %%' % {'pp':123.425556, } #字符串中包含%时需要输入2个%

print(s1,'\n',s2,'\n',s3,'\n',s4,'\n',s5,'\n',s6,'\n',)

#输出

i am jabe

i am jabe age 23

i am alex age 18

percent 99.98

i am 123.43 

i am 123.43 % 

2 format方式

 使用语法:[fill][align][sign][#][0][width][,][.precision][type]

  • fill   可选参数空白处填充的字符(比百分号方式突出的功能)
  • align  可选参数,对齐方式配合witdth使用
    • <,内容左对齐
    • >,内容右对齐(默认)
    • =,内容右对齐,将符号放置在填充字符左侧,且只对数字类型有效。即使:符号+数字
    • ^,内容居中(比百分号方式突出的功能)
  • sign 可选参数,有无符号数字
    • +,正号加正负号加负;
    • -,正号不变,负号加负
    • 空格,正号空格,负号加负
  • #,可选参数,对于二进制、八进制、十六进制,如果加上#,会显示0b/0o/0x,否则不显示
  • ,可选参数为数字添加分割符
  • witdth 可选参数,格式化位所占宽度
  • .precision可选参数 小数位保留精度
  • type可选参数 格式化类型
    • 传入字符串类型参数
      • s,格式化字符串类型数据
      • 空白,未指定类型默认为None
    • 传入整数型参数
      • b,将10进制整数自动转换成2进制表示然后格式化(比百分号方式突出的功能)
      • c,将10进制整数自动转换为其对应的unicode字符
      • d,十进制整数
      • o,将10进制整数自动转换成8进制表示然后格式化
      • x,将10进制整数自动转换成16进制表示然后格式化(小写x)
      • X,将10进制整数自动转换成16进制表示然后格式化(大写X)
    • 传入浮点型或小数型
      • e,转换为科学计数法(小写e)表示,然后格式化
      • E,转换为科学计数法(大写E)表示,然后格式化
      • f,转换为浮点型(默认小数点后保留6位)表示,然后格式化
      • F,转换为浮点型(默认小数点后保留6位)表示,然后格式化
      • g,自动在e和f中切换
      • G,自动在E和F中切换
      • %,显示百分比(默认显示小数点后6位,比百分号方式突出的功能

 format方式常用操作

s1 = "i am {}, age {}, {}".format("seven", 18, 'alex') #普通调用方式按顺序调用

s2 = "i am {}, age {}, {}".format(*["seven", 18, 'alex']) #调用列表中的值,列表前面需要加*

s3 = "i am {0}, age {1}, really {0}".format("seven", 18) #按照指定下标顺序带入参数

s4 = "i am {0}, age {1}, really {0}".format(*["seven", 18])#下标数字带入列表方式也需要加*

s5 = "i am {name}, age {age}, really {name}".format(name="seven", age=18) #根据变量名称方式带入参数

#以字典方式带入参数,前面需要加**
s6 = "i am {name}, age {age}, really {name}".format(**{"name": "seven", "age": 18})

s7 = "i am {0[0]}, age {0[1]}, really {0[2]}".format([1, 2, 3], [11, 22, 33])#列表嵌套方式带入

s8 = "i am {:s}, age {:d}, money {:f}".format("seven", 18, 88888.1) #s字符 d数字 f字符三种类型带入

s9 = "i am {:s}, age {:d}".format(*["seven", 18]) #列表方式带入 s d f三种类型变量

#定义名字和类型,按照名称方式引入变量
s10 = "i am {name:s}, age {age:d}".format(name="seven", age=18)

#按照字典方式导入,前面需要加**
s11 = "i am {name:s}, age {age:d}".format(**{"name": "seven", "age": 18})

#b二进制 o八进制 d十进制 x十六进制x小写 X16进制x大写 %转换为百分号形式方式转换
s12 = "numbers: {:b},{:o},{:d},{:x},{:X}, {:%}".format(15, 15, 15, 15, 15, 15.87623)

# 按照位置输出第一个位置参数,经过后面类型转换输出
s14 = "numbers: {0:b},{0:o},{0:d},{0:x},{0:X}, {0:%}".format(15)

#按照名称方式num来带入,经过后面类型转换输出
s15 = "numbers: {num:b},{num:o},{num:d},{num:x},{num:X}, {num:%}".format(num=15)

print(s1,'\n',s2,'\n',s3,'\n',s4,'\n',s5,'\n',s6,'\n',s7,'\n',s8,'\n',s9,'\n',s10,
      '\n',s11,'\n',s12,'\n',s14,'\n',s15)



#20宽度,s字符串类型,^居中方式显示,*其他地方用*补全,+d符号显示正号加+负号加-,x表示16进制方式显示
s = "----{:*^20s}====={:+d}==={:-d}======{: d}==== {:x}".format('jabe',123, -123, 123,15)
print(s)

#输出
i am seven, age 18, alex

i am seven, age 18, alex

i am seven, age 18, really seven

i am seven, age 18, really seven

i am seven, age 18, really seven

i am seven, age 18, really seven

i am 1, age 2, really 3

i am seven, age 18, money 88888.100000

i am seven, age 18

i am seven, age 18

i am seven, age 18

numbers: 1111,17,15,f,F, 1587.623000%

numbers: 1111,17,15,f,F, 1587.623000%

numbers: 1111,17,15,f,F, 1500.000000%

numbers: 1111,17,15,f,F, 1500.000000%

====      +123=====

----********jabe********=====+123===-123====== 123==== f

迭代器和生成器

迭代器

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件

特点:

  • 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
  • 不能随机访问集合中的某个值 ,只能从头到尾依次访问
  • 访问到一半时不能往回退
  • 便于循环比较大的数据集合,节省内存

生成器

定义:如果函数中包含yield语法,那这个函数就会变成生成器。

#生成器
def func():
    print(1111)
    yield 1
    yield 2
    yield 3

ret = func()

r1 = ret.__next__() #进入函数找到yield,获取yield后面的数据
print(r1)
r2 = ret.__next__() #进入函数找到yield,获取yield后面的数据
print(r2)
r3 = ret.__next__() #进入函数找到yield,获取yield后面的数据
print(r3)

上面的func即为一个生成器函数,下面调用__next__()函数为迭代器获取生成器内的每一个值,每次取到下一个值。

递归

如果一个函数在内部调用本身函数,这个函数就递归函数。

def func(n):

    n += 1

    if n >= 4:

        return  'end'

    return func(n)

ret = func(1)

print(ret)

上面的func就为递归函数,函数体中调用函数本身,每执行一次函数n值加1,判断n如果n>=4时就返回end值,因此print(ret) 就为end

递归实例:

应用递归工作原理来实现阶乘的计算1*2*3*4*5*6*7结果

def func(num):

    if num == 1:

        return  1

    return num * func(num - 1)

ret = func(7)

print('1*2*3*4*5*6*7 = %d'%ret)

#输出

1*2*3*4*5*6*7 = 5040

模块

模块,用一砣代码实现了某个功能的代码集合。
类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合。而对于一个复杂的功能来,可能需要多个函数才能完成(函数又可以在不同的.py文件中),n个 .py 文件组成的代码集合就称为模块。

所有模块都是先导入才能被调用使用

分类:

  • 自定义模块
  • 第三方模块
  • 内置模块

 自定义模块

1 定义模块

def f1():
    print('F1')

将以上简单的一个函数写入到common.py中这个common.py就可以称之为一个自定义的模块

2 导入模块

将以上模块编辑好怎么调用呢,现在就需要我们把上面编辑好的模块导入到现在的程序中

导入模块方有以下几种方式式:

  • import common 直接import+模块名称方法
  • import lib.common 如果common在lib目录下面的话可以采用这种方式导入
  • import lib 导入lib整个目录之后在程序中也可以调用common模块(不建议此种方式导入,因为在程序中可能会和导入模块的方法会有相同调用出问题)
  • from lib import common 也可以采用这种方式导入common模块
  • from lib import common as c 重命名方式,as后面加重新命名的名称(推荐导入方式)
  • from lib import * 与import lib 这种方式类似,将lib目录下面所有的模块加载到内存

导入模块实际上是告诉python解释器解释这个模块的py文件

那么问题来了,导入模块时是根据那个路径作为基准来进行的呢?

这个我们其实可以用python自带的sys模块可以查看路径信息

import  sys
print(sys.path)
#输出
['F:\\python\\s13\\day5', 'F:\\python\\s13\\day5', 
'C:\\Users\\jianbin\\AppData\\Local\\Programs\\Python\\Python35\\python35.zip',
'C:\\Users\\jianbin\\AppData\\Local\\Programs\\Python\\Python35\\DLLs',
'C:\\Users\\jianbin\\AppData\\Local\\Programs\\Python\\Python35\\lib', 
'C:\\Users\\jianbin\\AppData\\Local\\Programs\\Python\\Python35', 
'C:\\Users\\jianbin\\AppData\\Local\\Programs\\Python\\Python35\\lib\\site-packages']

sys模块为系统内置模块,sys.path就可以查看到系统的路径信息,首先会输出当前程序的目录位置,下面就是python的安装目录以及一些的自带的一些模块目录和安装后的第三方模块等目录位置,也就是说在这些路径下面的模块都可以直接imort 到程序中直接调用

如果sys.path中没有你想要的路径可以用过sys.path.append('路径')方法来添加

第三方模块

随着更多项目的开源,网上也有很多的开源模块可以供我们直接使用,我们下载安装上使用

安装方式

  • pip安装 python3自带了pip程序,无需我们在安装,通过pip方式安装的只需执行pip3 install 模块名,在linux或者ubuntu下可以用yum、pip和apt-get来安装
  • 源码安装
  1. 下载源码包
  2. 解压源码包
  3. 进入源码包目录
  4. 安装源码python setup.py install

以安装requests模块为例:

源码安装

pip3 install requests

#安装成功后会在%python_home%/lib/site-packages/路径下生成安装的模块

#测试安装是否成功,打开python执行终端输入

import requests #没有报错的话说明安装导入成功

源码安装

进入解压的目录

python3 setup.py install

内置模块

序列化模块pickle和json

对于一些的字符和列表字典元组等相互的转化操作,json模块和pickle是必备的神器

json:

  用于【字符串】和 【python基本数据类型】 间进行转换

  更加适合跨语言 处理字符串,基本数据类型

pickl:

  用于【python特有的类型】 和 【python基本数据类型】间进行转换 

  仅适用于python 处理python的所有类型的序列化

json模块四个功能:dumps、dump、loads、load

pickle模块四个功能:dumps、dump、loads、load

dump:主要是将字典列表等转换为字符串并存入到文件中

dumps:主要是将字典列表等转换为字符串不进行写文件的操作

load:从文件中读取出字符串转换为字典列表等

loads:将字形似字典列表等的字符串转换为相应的字典列表等

json和pickle练习

#调用接口返回字符串
import  json,requests

response = requests.get("http://wthrcdn.etouch.cn/weather_mini?city=北京")

response.encoding = 'utf-8'

dic1 = json.loads(response.text)

print(dic1)

#str_li = [11,22,33]
#li = json.dump(str_li,open('db','w')) #dump写入文件

li1 = json.load(open('db','r'))

print(li1,type(li1))

#输出
{'status': 1000, 'desc': 'OK', 'data': 
{'wendu': '19', 'ganmao': '各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。', 'yesterday': 
{'high': '高温 26℃', 'fl': '微风', 'date': '7日星期二', 'fx': '无持续风向', 'type': '多云', 'low': '低温 16℃'}, 
'city': '北京', 'forecast': [{'fengxiang': '无持续风向', 'high': '高温 26℃', 
'date': '8日星期三', 'fengli': '微风级', 'type': '多云', 'low': '低温 16℃'}, 
{'fengxiang': '无持续风向', 'high': '高温 31℃', 'date': '9日星期四', 'fengli': '微风级', 'type': '多云', 'low': '低温 19℃'}, 
{'fengxiang': '南风', 'high': '高温 32℃', 'date': '10日星期五', 'fengli': '3-4级', 'type': '多云', 'low': '低温 21℃'}, 
{'fengxiang': '无持续风向', 'high': '高温 30℃', 'date': '11日星期六', 'fengli': '微风级', 'type': '雷阵雨', 'low': '低温 20℃'}, 
{'fengxiang': '无持续风向', 'high': '高温 31℃', 'date': '12日星期天', 'fengli': '微风级', 'type': '多云', 'low': '低温 21℃'}],
 'aqi': '115'}}
[11, 22, 33] <class 'list'>


#pickle操作
import pickle

li = [11,22,33,45]

pickle.dump(li,open('db1','wb'))

#strli = pickle.dumps(li)

#print(strli)

#resault = pickle.loads(strli)

resault1 = pickle.load(open('db1','rb'))

print(resault1)

  

 

time 和datetime模块

time和datetime模块主要有如下功能:

#_*_coding:utf-8_*_
import time
import datetime
 
print(time.clock()) #返回处理器时间,3.3开始已废弃
print(time.process_time()) #返回处理器时间,3.3开始已废弃

print(time.time()) #返回当前系统时间戳

print(time.ctime()) #输出Tue Jan 26 18:23:48 2016 ,当前系统时间

print(time.ctime(time.time()-86640)) #将时间戳转为字符串格式

print(time.gmtime(time.time()-86640)) #将时间戳转换成struct_time格式

print(time.localtime(time.time()-86640)) #将时间戳转换成struct_time格式,但返回 的本地时间

print(time.mktime(time.localtime())) #与time.localtime()功能相反,将struct_time格式转回成时间戳格式

#time.sleep(4) #sleep

print(time.strftime("%Y-%m-%d %H:%M:%S",time.gmtime()) ) #将
struct_time格式转成指定的字符串格式

print(time.strptime("2016-01-28","%Y-%m-%d") ) #将字符串格式转换成struct_time格式
 
#datetime module
 
print(datetime.date.today()) #输出格式 2016-01-26

print(datetime.date.fromtimestamp(time.time()-864400) ) #2016-01-16 将时间戳转成日期格式

current_time = datetime.datetime.now() #

print(current_time) #输出2016-01-26 19:04:30.335935

print(current_time.timetuple()) #返回struct_time格式
 
#datetime.replace([year[, month[, day[, hour[, minute[, second[, microsecond[, tzinfo]]]]]]]])

print(current_time.replace(2014,9,12)) #输出2014-09-12 19:06:24.074900,返回当前时间,但指定的值将被替换
 
str_to_date = datetime.datetime.strptime("21/11/06 16:30", "%d/%m/%y %H:%M") #将字符串转换成日期格式

new_date = datetime.datetime.now() + datetime.timedelta(days=10) #比现在加10天

new_date = datetime.datetime.now() + datetime.timedelta(days=-10) #比现在减10天

new_date = datetime.datetime.now() + datetime.timedelta(hours=-10) #比现在减10小时

new_date = datetime.datetime.now() + datetime.timedelta(seconds=120) #比现在+120s
print(new_date)

logging模块

 很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出,python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志,logging的日志可以分为debug、info、warning、error、critical5个级别,下面我们看一下怎么用。

 简单用法:

import logging
 
logging.warning("user [alex] attempted wrong password more than 3 times")
logging.critical("server is down")
 
#输出
WARNING:root:user [alex] attempted wrong password more than 3 times
CRITICAL:root:server is down

把日志记录到文件中

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.')
 
#输出

12/12/2010 11:46:36 AM is when this event was logged.

同时将日志打印到文件和屏幕上

import logging
 
#create logger
logger = logging.getLogger('TEST-LOG')
logger.setLevel(logging.DEBUG)
 
 
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
 
# create file handler and set level to warning
fh = logging.FileHandler("access.log")
fh.setLevel(logging.WARNING)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
 
# add formatter to ch and fh
ch.setFormatter(formatter)
fh.setFormatter(formatter)
 
# add ch and fh to logger
logger.addHandler(ch)
logger.addHandler(fh)
 
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

  

 

 

 

  

 

posted @ 2016-06-06 13:38  Jabe  阅读(423)  评论(0)    收藏  举报