函数| 常用模块总结练习


### 第一模块内容

1. 请写出 “路飞学城alex” 分别用 utf-8 和 gbk 编码所占的位数(口述)
utf-8:一个中文字符占3个字节,英文字符占1个字节,则“路飞学城alex"一共16个字节,则共有16*8=128位。
gbk:一个中文占2个字节,英文字符占1个字节,则“路飞学城alex”一共12个字节,则共占12*8=96位

2. python有哪几种数据类型,分别什么?哪些数据类型是有序的。
基本类型有:数字(整数int、长整数long和浮点数float)、字符串(文本str和字节bytes)和布尔(True和False);
数据集有:列表list、元组tuple、字典dict和集合set。
有序的:字符串、列表、元组等

3. 列表和字典的pop方法有什么区别。
列表的pop()是删除最后一个;字典的pop()要制定k值的删除。

### 第一套题


1. 对文件"02第二模块之三体语录"进行增删改查 (文件操作编程)
''' 该语录视频没有讲解,我传到群文件了 直接发给学员 '''
  1. 查,使用seek(15),和f.read(2)是什么字符,直接口述。
    f = open('三体语录','r',encoding='utf-8')
    f.seek(15) 打印出15,seek是找的字节;f.read(2)打印出1、,read读的是字符

  2. 把第三行的“不要回答”替换成“绝对不能回复”

#方法一
import os f
= open('三体语录','r',encoding='utf-8') f_new = open('三体语录.new','w',encoding='utf-8') for line in f: if '不要回答' in line: line = line.replace('不要回答','绝对不能回复') f_new.write(line) f.close() f_new.close() os.rename('三体语录.new','三体语录')
#方法二
f = open('三体语录','r+',encoding='utf-8')
f1 = f.readline()
f.seek(0)
for line in f1:
    if '不要回答' in line:
        line_new = line.replace('不要回答','绝对不能回复')
        f.write(line_new)
f.close()

  3. 删掉最后一行

with open('三体语录','r',encoding='utf-8') as f:
    f1 = f.readlines()  ##readline是读一行 和readlines是读出所有行的区别
    with open('三体语录','w',encoding='utf-8')as f_w:
        for line in f1:
            if '25' in line:
                continue  #跳出本次循环
            f_w.write(line)

  4. 解释一下替换过程发生了什么事情。
    一种是在占硬盘的修改:以读的模式打开文件,再以写的模式打开一个新的文件;循环读文件,把要修改的替换掉;在新文件中写入每一行。
    另外一种是在内存中修改:以读写的模式打开,先每一行的读出来,再把光标移到最开始,循环读到内存的,然后替换写入文件

  5. 追加“给岁月以文明,而不是给文明以岁月”在第五行。(就是把其他的内容往下挤,不是覆盖第五行)

lines = []
f = open('三体语录','r',encoding='utf-8')
for line in f:
    lines.append(line)
lines.insert(5,'给岁月以文明,而不是给文明以岁月
')
s = ''.join(lines)
with open('三体语录','w',encoding='utf-8')as f_write:
    f_write.write(s)
    f_write.close()

  6. 最后一行替换成“给时光以生命,而不是给生命以时光”

lines = []
f = open('三体语录','r',encoding='utf-8')
for line in f:
    lines.append(line)
print(lines)
lines[24] = '25、给时光以生命,而不是给生命以时光'
s = ''.join(lines)
with open('三体语录','w',encoding='utf-8')as f_write:
    f_write.write(s)
    f_write.close()

2. 编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码

import time
a = time.localtime()
def logger(func):
    def inner():
        with open('用户名和密码','r',encoding='utf-8') as f:
            lis = eval(f.read())
        usename = input('请输入用户名:')
        passworld = input('请输入密码:')
        if usename == lis['_usename'] and passworld == lis['_passworld']:
            print('认证成功')
            func()
        else:
            print("您的输入有误")
    return inner
@logger
def log1():
    print("%s-%s-%s"%(a.tm_year,a.tm_mon,a.tm_mday))
def log2():
    time.sleep(2)
    print('%s-%s-%s' % (a.tm_year, a.tm_mon, a.tm_mday))
log1()
log2()

3. 函数的参数 (口述)
- 形参和实参的区别。
  形参变量:只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。
  实参可以是常量、变量、表达式、函数等,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
- 位置参数,默认参数,关键参数,非固定参数的顺序。
  位置参数、关键参数、默认参数、非固定参数
- 解释下参数的作用。
  参数可以让函数更灵活,还可以根据调用时传参的不同来决定函数内部的执行流程。
- 什么数据类型可以做实参。
常量、变量、表达式、函数等
4. 解释一下是否会报错,原因是什么?要是不报错打印什么值?报错的可以怎么改就不报错? (口述)

```
def test():
print(luffy)
luffy = "the king of sea."
```
不会报错,也不会打印,没有调用函数。
```
def test():
print(luffy)
luffy = 'e'
luffy = "the king of sea."
不会报错,也不会打印,因为没有调用函数。
```
```
def test():
luffy = 'e'
print(luffy)
luffy = "the king of sea."
```
不会报错不会打印,没有调用。

5. li = [1,2,3,5,5,6,7,8,9,9,8,3] 利用生成器功能,写一个所有数值乘以2的功能。(编程)

li = [1,2,3,5,5,6,7,8,9,9,8,3]
a = [i*2 for i in li]
print(a)

6. isinstance('s',str) 与 tupe('s') is str 效果是否一样?(口述)

  效果一样

7. 序列化-json,xml,pickle (口述)
  json和pickle的区别是什么?
    json:只支持str、int、tuple、list、dict ;
    pickle:支持python里的所有的数据类型;只能在python里使用。
    xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,通过<>节点来区别数据结构.
8. 描述写硬盘的编码转变(UTF-8格式,系统格式GBK) (口述)
  系统文件先解码decode为Unicode,再编码encode为GBK的格式

9. 解释以下代码含义 (口述)
```
from functools import reduce
reduce(lambda x,y:x+y,range(10))
```

from functools import reduce
reduce(lambda x,y:x+y,range(10))
print(reduce(lambda x,y:x+y,range(10)))

  从functools库中导入reduce模块,reduce函数会对参数序列中元素进行累积。
  reduce()函数返回一个值而不是一个list。首先需要定义一个需要对两个参数进行算数运算的函数。
  reduce()函数首先会对list中的第一个value和第二个value进行这个算数运算,这会得到一个result。
  之后它会对这个result和list中的第三个value进行这个算数运算得到一个新的result,最后得到最终的结果。

10. 打印日志11/26/2017 10:44:21 PM bug 24 并写入文件example.log中 (编程)

import logging
logging.basicConfig(filename='example.log',
level=logging.INFO,format='%(asctime)s %(message)s %(lineno)d',
datefmt='%m/%d/%Y %I:%M:%S %p')
                      # filemode=0o755,
logging.warning("bug")

顺便告诉学员linux创建目录时候可以使用filemode=0o755 来指定权限

### 第二套

1. 编写带参数装饰器auth,
装饰器参数engine='file'时,模拟用户名username='luffy',密码password='city'认证
装饰器参数engine='mysql'时,模拟用户名username='alex',密码password='3714'认证
错误达到三次时退出程序

def auth(engine):
    def outer(func):
        def inner(*args,**kwargs):
            i = 0
            while i < 3:
                username = input('Please input your username:')
                password = input('Please input your password:')
                if username == 'luffy' and password == 'city':
                    print('欢迎luffy登录成功')
                    res = func(*args,**kwargs)
                    return res
                    break
                elif username == 'alex' and password == '3714':
                    print('欢迎alex登录成功!')
                    resu = func(*args,**kwargs)
                    return resu
                    break
                else:
                    print('您的输入有误,请重新输入:')
                    i += 1
        return inner
    return outer
@auth(engine='file')
def login1():
    print('Login file successfully!')
@auth(engine='mysql')
def login2():
    print('Login mysql successfully!')
login1()
login2()

2. 解释一下“w”和“wb”的区别(“r”和“rb”,“a”和“ab”) (口述)
  f = open('三体语录', 'r' ,encoding='utf-8') mode=r表示只读模式
  f = open('三体语录', 'rb' ) mode=rb 表示二进制模式

  文件操作时,以 “a”或“ab” 模式打开,则只能追加,即:在原来内容的尾部追加内容
  写入到硬盘上时,必须是某种编码的0101010,打开时需要注意:
  ab,写入时需要直接传入以某种编码的0100101,即:字节类型;
  a 和 encoding,写入时需要传入unicode字符串,内部会根据encoding制定的编码将unicode字符串转换为该编码的 010101010
  f = open('三体语录', 'a' ,encoding='utf-8')
  f = open('三体语录', 'ab' ,encoding='utf-8')

3. 描述写硬盘的编码转变(UTF-8格式,系统格式GBK) (口述)
  系统文件先解码decode为Unicode,再编码encode为GBK的格式

4. 有两个磁盘文件A和B,各存放一行字母,要求把这两个文件中的信息合并(按字母顺序排列), 输出到一个新文件C中。(编程)

with open('test1','r',encoding='utf-8') as f1:
    a = f1.read()
f1.close()
f2 = open('test2','r',encoding='utf-8')
b = f2.read()
f2.close()

f3 = open('tes3','w',encoding='utf-8')
l = list(a + b)
l.sort()
s = ''
s = s.join(l)
f3.write(s)
f3.close()

5. 装饰器的必备条件是什么?(口述)
  装饰器本质为函数(用来装饰其他函数)添加附加功能
  1不能修改被装饰函数的源代码;2不能修改函数的调用方式
  高阶函数+嵌套函数 = 装饰器

6. 返回值 (口述)
- 默认的返回值是什么? None
- 返回值的个数限制? 没有
- 返回值的类型限制? 没有

7. 迭代器和生成器的区别, 在python中它们的原理是什么。(口述)

  生成器:generator是指这种一边循环一边计算的机制;
  迭代器:Iterator是指可以被next()函数调用并不断返回下一个值的对象。
  迭代器与生成器的区别:
    迭代器由Class对象创建. 生成器由包含yield表达的Function对象或者Generator Expression创建.
  迭代器的原理:
    (1)由Iterable.__iter__()返回Iterator.
    (2)由Iterator.__next__()返回迭代值, 直到StopIteration.
    一般迭代器同时实现__iter__()与__next__(), 在__iter__()返回self, 在__next__()返回迭代值,直到StopIteration
  生成器原理:
    (1)生成器都有next(), send(), close(), throw()
    (2)调用next()/send(V)执行到下一个yield表达式,并返回yield表达式结果. 如果是send(V)将用V代替yield表达式的值往下执行, next()等价send(None)
    (3)生成器不允许return任何值.
8. str,list,tuple,len 方法的作用及其它们的区别在哪里?
    str()转成字符串,list()转成列表,tuple()转成元组,len()返回长度或个数


9. enumerate的作用是什么?(口述)
  enumerate可以遍历索引又可以遍历元素;enumerate还可以接收第二个参数;用于指定索引起始值如下面例子enumerate(list1,1);
  可以遍历字符串、列表、字典、数组等

list1 = [4,5,6,3,9,]
for index, item in enumerate(list1):
    print(index, item)

10. 以下两次打印是否一致,一致请说明缘由,不一致请问如何修改使两次打印一致。 (口述)
```
import hashlib
m = hashlib.md5()
m.update(b'luffy')
m.update(b' city')
print(m.hexdigest())
m2 = hashlib.md5()
m2.update(b'luffycity')
print(m2.hexdigest())
```
    重复调用update(arg)方法,是会将传入的arg参数进行拼接,而不是覆盖。
    也就是说,m.update(a); m.update(b) 等价于m.update(a+b)

import hashlib
m = hashlib.md5()
m.update(b'luffy')
m.update(b' city')
print(m.hexdigest())

print(hashlib.md5(b'luffycity').hexdigest())

### 第三套

1. 字符串“Luffy”,将小写字母全部转换成大写字母,将大写字母转换成小写字幕,然后输出到一个磁盘文件"test"中保存。(编程)

s = 'Luffy'
with open('test','w',encoding='utf-8') as f:
    f.write(s.swapcase())


2. 描述写硬盘的编码转变(UTF-8格式,系统格式GBK) (口述)
  系统文件先解码decode为Unicode,再编码encode为GBK的格式

3. 作用域 (口述)
1. 什么是函数的作用域,举个例子说明一下。
  限定这个名字的可用性的代码范围就是这个名字的作用域。
  在python中一个函数就是一个作用域。无论在任何地方调用这个函数,永远回到它最开始的地方从上执行,往上找。
  python中函数就是一个作用域,局部变量放置在其作用域中;代码完成后,作用域已经生成。
  LEGB 代表名字查找顺序: locals ->enclosing function -> globals -> __builtins__
    locals 是函数内的名字空间,包括局部变量和形参
    enclosing 外部嵌套函数的名字空间
    globals 全局变量,函数定义所在模块的名字空间
    builtins 内置模块的名字空间

a = 8
b = 10
def outer():
    a = 0
    b = 1
    def inner():
        a = 10
        b = 11
    inner()
outer()
print(a,b)

2. 以下代码最终结果是什么?

```
a = 8
b = 10
def outer():
a = 0
b = 1
def inner():
a = 10
b = 11
inner()
outer()
print(a,b)
```
打印出:8 10

```
# 考察内存空间

a = [1,2,3]
def outer():
    a.append(5)
outer()
print(a)

```
打印出:[1,2,3,5]

4. 迭代器带来的好处是什么 (口述)
  使用迭代器不要求事先准备好整个迭代过程中的所有元素。
  迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后元素可以不存在或者被销毁。
  因此迭代器适合遍历一些数量巨大甚至无限的序列。


5. 写一个带参数的用户认证

def auth_argu(t):
    def auth(func):
        def wrapper(*args,**kwargs):
            user = input("请输入用户名:").strip()
            pas = input('请输入密码:').strip()
            if user == 'luffy' and pas == 'city':
                res = func(*args,**kwargs)
                return res
        return wrapper
    return auth
@auth_argu(t = '传参')
def log1():
    print('登录成功')
log1()

6. 装饰器的原理是什么, 请简要说一下一个函数加一个装饰器执行的流程.(口述)
  装饰器decorator只是将函数作为参数的函数。通常,它的作用是返回封装好的经过修饰的函数.
  装饰器在执行的时候,把函数test1当做参数传给了timer函数,timer函数执行结果就是返回了一个func的内存地址
  执行函数test1的时候相当于是在调用deco

import time
def timer(func): #timer(test1) | func=test1,把test1的内存地址传给了func;然后开始执行timer这个函数,只是声明了一个变量。
  def deco(): ##timer(test1)函数的执行结果就只返回了一个deco的内存地址
    start_time = time.time()
    func() #运行test1()
    stop_time = time.time()
    print('the func run time is %s'%(stop_time - start_time))
  return deco
@timer #--->> test1 = timer(test1) test1就相当于timer(test1)
def test1():
  time.sleep(2)
  print('in the test1')
test1() ##test1执行相当于调用的是deco,执行的是deco这个函数

7. 利用sys模块和os模块 py/bin/start.py (口述)

```
[root@web01 py]# tree
.
├── bin
│   └── start.py
├── conf
│   └── account.ini
└── core
└── main.py
```
在start.py执行以下代码,请阐述一下代码含义。
```
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
```
    BASE_DIR是绝对路径。sys.path.append(BASE_DIR) 把它加到环境变量里边
    程序在哪执行,当前路径就是那个,要想导入其他模块就要找到它的路径。

8. 利用内置函数chr(),ord()以及random模块写一个简单随机4位验证码 (编程)
chr 把ascii码换成对应的符号,ord把符号换成ascii码
方法1:

import random
checkcode = ''
for i in range(4):
    current = random.randrange(0,4)
    if current != i:
        temp = chr(random.randint(65,90)) #chr把ascii码换成对应的符号
    else:
        temp = random.randint(0,9)
    checkcode += str(temp)
print(checkcode)

方法2::

import random,string
source =  string.digits + string.ascii_lowercase
print("".join(random.sample(source,4)))


9. 写一个map函数 (编程)

a=map(lambda x:x+1 if x > 5 else x**2 ,[1,2,3,6,7,8])
for i in a:
    print(i)


10. 解释一下包和模块的含义。 (口述)

  一个文件夹管理多个模块文件,这个文件夹就被称为包。 __init__ 把目录初始化成一个包。
  在Python中,一个.py文件就称之为一个模块(Module)

### 第四套
1. 描述写硬盘的编码转变(UTF-8格式,系统格式GBK) (口述)
  系统文件先解码decode为Unicode,再编码encode为GBK的格式

2. 编写sed功能,使用下面执行命令之后,“修改前的内容” 会被修改成 “修改后的内容”,该内容使用re模块进行匹配,不允许使用replace。 (编程)

```python
python3 sed.py 's#修改前的内容#修改后的内容#g' 修改文件名。
```

import sys,re
msg = sys.argv[1]  #sys.argv 命令行参数List,第一个元素是程序本身路径
filename = sys.argv[2]
    # msg = '  's#修改前的内容#修改后的内容#g'
old = re.search("'s#[^#]+#", msg).group()[3:-1]
new = re.search("#[^#]+#g", msg).group()[1:-2]
with open(filename,'r',encoding='utf-8')as f_read:
    with open(filename+".new","w",encoding='utf-8') as f_write:
        date_read = f_read.read()
        date_write = re.sub(old,new,date_read)
        f_write.write(date_write)


3. 简述内置函数globals() locals()的作用 (口述)

  globals()获取当前代码里的所有全局变量;locals()获取当前代码里的所有局部变量

a = 12
b = 456
c = 789
d = globals()
print(d)

def f1():
    a = 123
    b = 456
    c = 789
    print(locals())
f1()

4. 利用内置函数 filter 和自定义函数获取l1大于33的所有元素 l1 = [11, 22, 33, 44, 55] (编程)

l1 = [11, 22, 33, 44, 55]
for i in filter(lambda x:x>33,l1):
    print(i)


5. 编写3个函数,每个函数执行的时间是不一样的,
提示:可以使用time.sleep(2),让程序sleep 2s或更多。

import time
def timer(func):  #timer(test1)  func=test1
    def deco():
        start_time = time.time()
        func()  #运行test1()
        stop_time = time.time()
        print('the func run time is %s'%(stop_time - start_time))
    return deco
@timer
def test1():
    time.sleep(2)
    print('in the test1')
@timer
def test2():
    time.sleep(3)
    print('in the test2')
@timer
def test3():
    time.sleep(4)
    print('in the test3')
#test1 = timer(test1)
test1()
#test2 = timer(test2)
test2()
test3()


6. 执行f.write()之后,会立刻写入磁盘吗?不会的话,有几种方式能让其写入磁盘中? (口述)

  执行write方法之后,会写在内存当中,想写入磁盘有三种方案 (写入缓冲区,解决程序写入硬盘慢的问题)
    1. f.flush(),打印实时日志使用
    2. f.close()
    3. 关闭程序

7. 函数闭包 (口述)
创建一个闭包函数需要满足哪几点:
    必须有一个内嵌函数
    内嵌函数必须引用外部函数中的变量
    外部函数的返回值必须是内嵌函数

8. 写一个简单的加减乘除的函数。 (编程)

def calc(x,y):
    print(x+y)
    print(x-y)
    print(x*y)
    print(x//y)
calc(9,3)


9. 利用hashlib模块和open函数写一个三次验证代码,要求如下:(编程)
  1. 密码是加密的。
  2. 三次登陆失败锁定账号在文件当中。
  3. 登陆成功:使用一个全局变量进行记录

文件:正确用户信息 {'alex':'e99a18c428cb38d5f260853678922e03','kris':'202cb962ac59075b964b07152d234b70'}
锁定信息 []

i = 0
import hashlib
file = open("正确用户信息",'r',encoding='utf-8')
file_wrong = open("锁定信息",'r+',encoding='utf-8')
line = eval(file.readline())
line_wrong = eval(file_wrong.readline())

name = input("请输入用户名:")
passworld = input("请输入密码:")
m = hashlib.md5()
m.update(passworld.encode())
if name in line and name not in line_wrong:
    while i <= 3:
        if m.hexdigest() == line[name]:
            print('登录成功')
            break
        else:
            i +=1
            print('
您已经第%d次输错密码,输错3次账户将会被锁定'%(i))
            if i < 3:
                passworld = input("请重新输入密码:")
            elif i == 3:
                print("已经输错3次,账户已被锁定")
                line_wrong.append(name)
                file_wrong.seek(0)
                file_wrong.write(str(line_wrong))
                #file_wrong.tell()
                break
elif name in line_wrong:
    print("该用户名已被锁定")
else:
    print("该用户名不存在")
    exit()


10. time、datetime
将时间打印出成一个2017/10/01 18:08:15的格式
将 "2017-11-18" 17:43:43" 转换为 datetime的结构化时间
import time
print(time.strftime('%Y/%m/%d %H:%M:%S'))

import datetime
d = datetime.datetime.now()
print(d)
print(d.day)
print(datetime.datetime(2017,11,18,17,43,43))

### 未整理
1. 编写装饰器,为每个函数加上统计运行时间的功能
提示:在函数开始执行时加上start=time.time()就可纪录当前执行的时间戳,函数执行结束后在time.time() - start就可以拿到执行所用时间

见上

2. 编写装饰器,为函数加上认证的功能,即要求认证成功后才能执行函数

import time
a = time.localtime()
def logger(func):
    def inner():
        _usename = 'kris'
        _passworld = 'abc123'
        usename = input('请输入用户名:')
        passworld = input('请输入密码:')
        if usename == _usename and passworld == _passworld:
            print('认证成功')
            func()
        else:
            print("您的输入有误")
    return inner
def log1():
    print("%s-%s-%s"%(a.tm_year,a.tm_mon,a.tm_mday))#13
@logger
def log2():
    time.sleep(2)
    print('%s-%s-%s' % (a.tm_year, a.tm_mon, a.tm_mday))
@logger
def log3():
    time.sleep(4)
    print('%s-%s-%s' % (a.tm_year, a.tm_mon, a.tm_mday))
log1()
log2()
log3() 


3. read,readline,readlines的区别 (口述)
  

  read([size])从文件当前位置起读取size个字节,若无参数size,则表示读取至文件结束为止。它的返回是字符串对象。
  readline每次读取一行内容,所以读取时占用内存小,比较适合大文件。readline返回一个字符串对象。
  readlines读取文件的所有行,保存在一个列表(list)变量中。每1个文件行作为一个list元素,但读取大文件会比较占内存。


4. 模拟登陆 (编程)
```
用户输入帐号密码进行登陆
用户信息保存在文件内
用户密码输入错误三次后锁定用户,下次再登录,检测到是这个用户也登录不了
```

见上


5. 从键盘输入一些字符,逐个把它们写到磁盘文件上,直到输入一个 # 为止。 (编程)

filename = input('输入文件名:')
f = open(filename , "w+")
ch = ''
while '#' not in ch:
    f.write(ch)
    ch = input('输入字符串:
')
f.close()



6. 函数的作用(特性/好处).(口述)
  函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可
  特性:减少重复代码;使程序变的可扩展;使程序变得易维护。

7. 递归函数 (编程)
实现递归函数的条件有那些?
递归实现阶乘1*2*3*4*5*6*7 (获取最终阶乘的答案)
  必须有一个明确的结束条件;每次进入更深一层递归时,问题规模相比上次递归都应有所减少

def calc(n):
    if n < 2:
        return 1
    else:
        return n *calc(n-1)
print(calc(7))

1. 快速找到26,不使用index等内置方法。二分法使用(编程)
[1,4,6,8,9,11,26,34,56,58,72,88]

data = [1,4,6,8,9,11,26,34,56,58,72,88]
if len(data) >1:
    mid = int(len(data)/2)
    if data[mid] == 26:  #find it
        print("找到数字",data[mid])
    elif data[mid] > 26:
        print('我找的数在左边')
###########################
data = [1,4,6,8,9,11,26,34,56,58,72,88]
def binary_search(dataset,find_num):
    print(dataset)
    if len(dataset) >1:
        mid = int(len(dataset)/2)
        if dataset[mid] == find_num:  #find it
            print("找到数字",dataset[mid])
        elif dataset[mid] > find_num :# 找的数在mid左面
            print("