python的重定向问题

1 基础概念

1.1 标准流

sys模块提供了python的标准输入(sys.stdin),标准输出(sys.stdout),错误流(sys.stderr)。print和input只是标准输出/输入流的接口。
# 标准输出例子
print('Hello stdout world')
# 等价于
sys.stdout.write('Hello stdout world' + '\n')

# 标准输入例子
input('hello stdin world')
# 等价于
sys.stdin.readline()[:-1]  # 直接接受输入,省略了输入说明

1.2 重定向流到文件或程序

在默认的情况下,标准的输出(以及print)会打印到程序启动的console窗口上,标准输入(以及input)的文本从键盘读取,标准错误来向console窗口打印python错误信息。

流可以重定向到:文件、系统shell的其他程序。
# 一个重定向简单例子
def interact():
    print('hello stream world')
    while(True):
        try:
            reply=input('Enter a number>')
        except EOFError:                   # 输入Ctrl-Z可以触发该错误,windows下
            break
        else:
            num=int(reply)
            print("%d squared is %d" % (num, num**2)
    print('Bye')
    
if __name__ == "__main__":
    interact()
在shell语法中, < filename 把标准输入流定向到文件输入, > filename 把标准输出流定向到文件中。

1.3 用管道(pipe)链接程序

在shell命令中,“|”可以将一个程序的标准输出流发送到另一个程序的标准输入流。这种操作被称为是“管道操作”。
# reader.py
print('Got this: "%s" % input())
import sys
data = sys.stdin.readline()[:-1]
print('The meaning of life is', data, int(data) *2)
# write.py
Help!Help!I'm being repressed!
42
在shell命令中输入 python writer.py | reader.py    [要注意,python已经在系统环境变量中,当前工作路径为reader.py所在文件夹]

2 重定向流到Python对象

在Python里,任何在方法上与文件类似的对象都可以充当标准流,和对象的数据类型无关,而取决于接口(协议)。详细说明:

任何提供类似文件 read方法 的对象可以指定给sys.stdin,以从该对象的read方法读取输入
任何定义类似文件 write方法 的对象可以指定给sys.stdout,所有的标准输出将发动到该对象的write方法上
print和input只是简单的调用sys.stdout和sys.stdin所引用对象的wirte和readlinne方法。

import sys

class Output:
    
    def __init__(self):
        self.text = ''
        
    def write(self, string):
        self.text += string
        
    def writelines(self, lines):
        for line in lines:self.write(line)
        
class Input:
    def __init__(self, input=''):
        self.text = input
    
    def read(self, size=None):
        if size ==None:
            res,self.text = self.text, ''
        else:
            res,self.text = self.text[:size], self.text[size:]
        return res
        
    def readline(self):
        eoln = self.text.find('\n')
        if eoln ==-1:
            res,self.text = self.text, ''
        else:
            res,self.text = self.text[:eoln+1], self.text[eoln+1:]
        return res
        
def redirect(function, pargs, kargs, input):
    savestreams = sys.stdin, sys.stdout
    sys.stdin = Input(input)
    sys.stdout = Output()
    try:
        result = function(*pargs, **kargs)          # 这里有点关于参数 *参数 **参数的使用方法
        output = sys.stdout.text
    finally:
        sys.stdin, sys.stdout = savestreams
    return (result, output)

3 print 调用重定向语法

print(stuff, file = afile)

其中afile是一个文件对象,也可以把文件对象替换成sys.stdout 和 StringIO() 对象,看下面例子:
from io import StringIO

buff = StringIO()        # 一个对象,可读可写可缓存
print(42, file = buff)
print('spam', file = buff)
print(buff.getvalue())
>>>42
spam

form redirect import Output
buff = Output()
print(43, file=buff)
print('egg', file = buff)
print(buff.text)
>>>43
eggs

额外话题:对于参数的说明,对于 ** 和 *的作用说明探究

参考资料:

# 在函数调用中使用 * 解包参数
## 在调用函数时,* 操作符可以用于将可迭代对象解包至函数调用的参数中
### 例子1
lst = [1,2,3,4]
print(lst[0], lst[1], lst[2], lst[3])
>>>1 2 3 4
print(*lst)
>>>1 2 3 4

## ** 操作符,使用关键字参数,允许我们使用键-值对字典,并在函数调用中将其解包为关键字参数
### 例子3 【这是一个代码效果概览】
d = {'a':1, 'b':2, 'c':3}
f = **d
print(f)

string = "{a}-{b}-{c}".format(**dic)
>>>'1-2-3'

# * 和 ** 都可以多次使用
###
>>> strlist = ['a', 'b', 'c', 'd']
>>> numbers = [2, 1, 3, 4, 7]
>>> print(*numbers, *strlist)
2 1 3 4 7 a b c d

>>> dic = {'a': 1, 'b': 2, 'c': 3}
>>> dic2 = {'e': "Python", 'f': 'everyday'}
>>> string = "{a}-{b}-{c}-{e}-{f}".format(
...     **dic,
...     **dic2,
... )
>>> string
'1-2-3-Python-everyday'
说明:在多次使用时,python中的函数不能多次指定相同的关键词参数,因此**一起使用的每个字典的键必须不同。

*args 和 **kwargs 的区别
*args用来传递一个非键值对的可变数量的参数列表给一个函数。

def function(x,y,*args):
    print(x,y,args)
    
function(1,2,3,4,5)
>>>1 2 (3,4,5)
要注意,这里是将传进来的参数 3 4 5 打包成了一个元组。



**kwargs允许将不定长度的键值作为参数传递给一个函数。如果你想要在一个函数处理带别名子(关键字)的参数,应该使用**kwargs。
def function(**kwargs):
    print(kwargs)
    
# 注意传参的形式
function(a=1,b=2,c=3)
>>>{'a': 1, 'b': 2, 'c': 3}
当定义参数使用**kwargs时,传递的参数必须是解包过的字典,即带名字的参数。
# 定义参数的例子
def f(**pars):
    for key,value in pars.items():
        print('k = %s, v = %s' % (key, value))
        
b = {'b':1, 'c':2}
f(**b)

对于参数传递来说,*和**都是用来解包的.

对于参数定义的时候,*和**都是用来打包的。两者应用在对于参数个数不确定的情况下。