17

在 Python 3.5.0 上运行 Django v1.10:

from django.core.management.base import BaseCommand

class Command(BaseCommand):
    def handle(self, *args, **options):
        print('hello ', end='', file=self.stdout)
        print('world', file=self.stdout)

预期输出:

hello world

实际输出:

hello 

world

如何正确传递结束字符?我目前使用明确设置的解决方法:

 self.stdout.ending = ''

但是这个 hack 意味着你没有得到 print 函数的所有特性,你必须self.stdout.write手动使用和准备字节。

4

3 回答 3

12

正如Django 1.10 的自定义管理命令文档中所述:

当您使用管理命令并希望提供控制台输出时,您应该写入self.stdoutself.stderr,而不是直接打印到stdoutstderr。通过使用这些代理,测试您的自定义命令变得更加容易。另请注意,您不需要以换行符结束消息,它将自动添加,除非您指定结束参数

self.stdout.write("Unterminated line", ending='')

因此,为了在您的Command课程中打印,您应该将您的handle()函数定义为:

from django.core.management.base import BaseCommand

class Command(BaseCommand):
    def handle(self, *args, **options):
        self.stdout.write("hello ", ending='')
        self.stdout.write("world", ending='')

# prints: hello world

此外,通过显式设置self.stdout.ending = '',您正在修改self.stdout对象的属性。但您可能不希望这反映在未来的self.stdout.write(). 因此,最好ending在函数中使用参数self.stdout.write()(如上面的示例代码所示)。

正如您所提到的“但是这种 hack 意味着您没有获得打印功能的所有功能,您必须使用 self.stdout.write 并手动准备字节。” 不,您不必担心创建 的bytes或其他功能print(),因为self.stdout.write()属于OutputWrapper类的函数需要数据str格式。另外我想提一下,print()并且OutputWrapper.write()作为包装器的行为非常相似sys.stdout.write()

print()和之间的唯一区别OutputWrapper.write()是:

  • print()接受消息字符串作为参数来连接多个字符串,*argsseparator
  • OutputWrapper.write()接受单个消息字符串

但是这种差异可以通过使用分隔符显式连接字符串并将其传递给OutputWrapper.write().

结论:您不必担心提供的附加功能,print()因为没有,并且应该继续使用自定义管理命令文档self.stdout.write()中此答案的引用内容中的建议。

如果你有兴趣,你可以查看源代码BaseCommandOutputWrapper可用的类:Source code fordjango.core.management.base . 它可能有助于消除你的一些疑虑。您还可以查看与 Python 3 相关的PEP-3105print()

于 2016-10-08T11:08:05.960 回答
8

显式设置时self.stdout.ending,打印命令按预期工作。

行尾需要设置在self.stdout.endingwhenfile=self.stdout中,因为那是django.core.management.base.OutputWrapper.

class Command(BaseCommand):
    def handle(self, *args, **options):
        self.stdout.ending = ''
        print('hello ', end='', file=self.stdout)
        print('world', file=self.stdout)

退货

hello world
于 2016-10-07T21:38:34.537 回答
6

首先,self.stdout是一个django.core.management.base.OutputWrapper命令实例。它write期望一个str, not bytes,因此您可以使用

self.stdout.write('hello ', ending='')
self.stdout.write('world')

实际上self.stdout.write确实接受字节,但只有当它ending 一个空字符串时 - 那是因为它的write方法已定义

def write(self, msg, style_func=None, ending=None):
    ending = self.ending if ending is None else ending
    if ending and not msg.endswith(ending):
        msg += ending
    style_func = style_func or self.style_func
    self._out.write(force_str(style_func(msg)))

如果ending为真,则如果是实例且结尾是,msg.endswith(ending)则将失败。msgbytesstr

此外,当我明确设置时, printwithself.stdout确实可以正常工作;self.stdout.ending = ''但是这样做可能意味着其他使用self.stdout.write期望它插入换行符的代码会失败。


在您的情况下,我要做的是为以下定义一个print方法Command

from django.core.management.base import OutputWrapper

class PrintHelper:
    def __init__(self, wrapped):
        self.wrapped = wrapped

    def write(self, s):
        if isinstance(self.wrapped, OutputWrapper):
            self.wrapped.write(s, ending='')
        else:
            self.wrapped.write(s)

class Command(BaseCommand):
    def print(self, *args, file=None, **kwargs):
        if file is None:
            file = self.stdout
        print(*args, file=PrintHelper(file), **kwargs)

    def handle(self, *args, **options):
        self.print('hello ', end='')
        self.print('world')

你可以把它变成你自己的BaseCommand子类——你也可以将它与不同的文件一起使用:

    def handle(self, *args, **options):
        for c in '|/-\\' * 100:
            self.print('\rhello world: ' + c, end='', file=self.stderr)
            time.sleep(0.1)
        self.print('\bOK')
于 2016-10-07T19:43:00.250 回答