(六)Python对象的重定向

标准流重定向为Python对象

前面介绍过Python流的重定向,但都是在命令行窗口中实现的,不是在同一个Python脚本中做到的。如何在同一个Python脚本中实现标准流的重定向呢?这是本文将要探讨的问题。

标准流对象是在运行Python脚本或者启动Python控制台窗口时通过open函数创建的文件对象的引用。既然是引用,就可以重新进行赋值。被赋值的对象需要满足以下要求:
  • 任意提供read()方法的对象都可以赋值给sys.stdin,这样调用sys.stdin.read()就会调用该对象的read()获取数据
  • 任意提供write()方法的对象都可以赋值给sys.stdout,这样所有的标准输出都可以发送到该对象的方法中。
这样的对象又称为仿文件对象(file-like object)。
在Python的官方文档中有这样一句描述:“If it looks like a duck and quacks like a duck, it must be a duck.”(看起来像一只鸭子,叫声也像一只鸭子,那么它就是一只鸭子——有点像绕口令。)这种性质在Python中称为 polymorphism。从编程的角度来解释,就是如果一类对象它们的属性名和属性行为相似,那么就可以把它们当做相同的类型来使用,无论这些对象是函数,还是类。
根据以上原则,仿文件对象就是文件对象,可以通过read()/readline()/write()等文件方法进行文本的读写操作。
需要指明的是:
  • stdin处理所有用户交互的输入(包括调用内建函数input(),同样也会使用stdin)
  • stdout处理print()函数的输出(控制台的回显估计也是用这个函数实现的)和input()表达式中的输出(input(str),str不为None时,函数内部会调用print(str)打印提示信息,再等待用户的输入,用户输入完之后还会回显)
  • stderr处理Python解析器自身的指令和错误信息
  • 在版本2.7.2中,input(),raw_input()函数内部会调用sys.stdin.readline()方法;在版本3.x中,raw_input()和input()的功能合并,只保留了input(),input()函数内部仍然会调用sys.stdin.readline()方法。而print()函数内部调用sys.stdout.write()方法。
可以通过程序进行简单的测试(测试版本Python 3.3.5 shell)。
>>> class Input(object):
    def __init__(self, inStr = ''):
        self.txt = inStr
    def read(self, size = None):
        if size:
            block, self.txt = self.txt[:size], self.txt[size:]
        else:
            block, self.txt = self.txt, ''
        return block


>>> sys.stdin = Input('hello,world')
>>> input('Enter:')
Enter:Traceback (most recent call last):
  File "<pyshell#6>", line 1, in <module>
    input('Enter:')
AttributeError: 'Input' object has no attribute 'readline'
由于类Input没有提供readline方法,所以执行input()时失败
>>> class Input(object):
    def __init__(self, inStr = ''):
        self.txt = inStr
    def read(self, size = None):
        if size:
            block, self.txt = self.txt[:size], self.txt[size:]
        else:
            block, self.txt = self.txt, ''
        return block
    def readline(self):
        eoln = self.txt.find('\n')
        if eoln == -1:
            line, self.txt = self.txt, ''
        else:
            line, self.txt = self.txt[:eoln + 1], self.txt[eoln + 1:]
        return line

>>> sys.stdin = Input('hello,world')
>>> input("Enter: ")
Enter: 'hello,world'
以上脚本在v2.7.2的控制台窗口中执行时,必须用raw_input()代替input(),不然会报错。
将类的对象赋值给sys.stdin,使得input()直接调用类的readline()方法。不过如果只调用input(),则类中含有readline()方法就可以了,如:
>>> class Input:
	def readline(self):
		return "hello,world"

	
>>> sys.stdin = Input()
>>> input("Enter: ")
Enter: 'hello,world'
需要注意的是在控制台修改sys.stdin/sys.stdout引用之前,最好先保存下原有的流对象,对流对象操作结束之后,再将保存的对象替换回去。如果不这样,就会导致控制台无法回显;quit(),exit()等方法也会失效。如下面的例子:
>>> import sys
>>> save = sys.stdout
>>> class Output(object):
    def __init__(self):
        self.txt = ''
    def write(self, txt):
        self.txt += txt
    def writelines(self, txt):
        string = ''.join(txt)
        self.write(txt)


>>> obj = sys.stdout = Output()
>>> print("hello,world")
>>> 1 + 2
>>> "ni hao"
>>> sys.stdout = save
>>> obj.txt
"hello,world\n3\n'ni hao'\n"
可以看出修改了sys.stdout之后,控制台无法回显,需要回显的字符都存储在了obj.txt中。只有在恢复stdout的本来流对象之后,才可以正常回显了。
Python中的io模块提供了工具类StringIO和BytesIO,自动完成了上述的重定向的工作。可以不用自己敲代码了,因而十分便利。
例如:
>>> import sys
>>> temp = sys.stdout
>>> buffer = StringIO()
>>> sys.stdout = buffer
>>> print("hello,","world")
>>> sys.stdout = temp
>>> buffer.getvalue()
'hello, world\n'

Print()函数的重定向

print()函数在默认的情况下将文本输出到sys.stdout中,可以用一个仿文件对象替换它进行重定位。这样不用修改sys.stdout也可达到前文提到的重定位的效果。具体演示如下:
>>> class Output:
	def __init__(self):
		self.txt = ''
	def write(self, Str):
		self.txt += Str
	def getValue(self):
		return self.txt

	
>>> buffer = Output()
>>> print("hello", file = buffer)
>>> print("world", file = buffer)
>>> buffer.getValue()
'hello\nworld\n'

os.popen()的重定向

第三节中提到popen()方法创建对象时的模式,默认情况下是只读的("r"),可以修改模式为"w"。这样,可以在Python脚本中或者控制台窗口给命令行中的脚本传递文本信息,而无需借助命令行参数。
test_popen.py:
"""test the os.popen"""
def Test():
        open(r'D:\1.txt').write((input()) #不要使用print(input())或者sys.stdout.write(input())
Test()
在MS-DOS窗口执行Python,调出Python命令行窗口,然后敲入以下代码:
>>> import os
>>> f = os.popen('python test_popen.py', 'w')
>>> f.write('hello,world')#将"hello,world"传递给脚本test_popen.py,因为f.write会修改sys.stdout的行为,所以会导致print()和sys.stdout.write()函数失效
11
>>> f.close()
>>>
打印文件"D:\1.txt"的内容:


os.popen()的重定向和前面提到的标准流的重定向意义是不一样的。标准流的重定向是将输入输出流的目标指定为某个Python对象,这种重定向发生在同一个进程内;os.popen()可以实现不同进程间的信息通信。比如上例中,通过设定写模式,可以将Python控制台的文本传递test_popen.py脚本,运行时,它们分属两个不同进程。类似的Python函数还有subprocess.call()和subprocess.Popen(),比起os.popen(),函数subprocess.Popen在进程通信的控制上更为细致。
可以通过控制台传递文本给脚本:

也可以将脚本执行的结果传递给控制台:
print_helloworld.py:
print "hello,world"
控制台的执行结果:

脚本之间相互传递文本:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值