关于什么是正确的' pythonic'以下功能的方式。我是否必须将其拆分为两个函数?
def readSomething(fp=None):
if fp:
return fp.read(100)
else:
with open('default.txt', 'r') as fp:
return fp.read(100)
我需要这样的东西,因为readSomething
函数可以从另一个可能打开或不打开相同文件的函数中调用。
例如,在某些地方可能会这样调用:
def doSomethingWithSameFile():
with open('default.txt') as fp:
preample = fp.read(10)
more_data = readSomething(fb)
...
或者像其他地方一样:
def init():
data = readSomething()
...
答案 0 :(得分:4)
我不认为这是正确的解决方案,但我认为这是你想要的。
import contextlib
def readSomething(fp=None):
with contextlib.ExitStack() as stack:
if not fp:
fp = stack.enter_context(open('default.txt'))
return fp.read(100)
我得到的印象是你要复制这个逻辑很多函数,比如readSomething()
,所以我建议把ExitStack代码放到装饰器中,并将函数包装到你需要这种行为的地方。
您也可以使用装饰器。我不使用这种代码,因此下面的语法几乎肯定是不完整的,但总体思路是:
import functools
def fallback_to_default(fn):
@functools.wraps(fn)
def new_fn(fp=None, *args, **kwargs):
with contextlib.ExitStack() as stack:
if not fp:
fp = stack.enter_context(open('default.txt'))
return fn(fp, *args, **kwargs)
return new_fn
@fallback_to_default
def readSomething(fp=None):
return fp.read(100)
答案 1 :(得分:3)
用简明的语言概括这个问题:
这是在Python中接受不同的参数类型的问题。你被允许这样做,但它有时会让你的代码变得更丑陋。
上下文管理器只是try / finally的语法糖:
def readSomething(fp=None):
close_fp = False
if fp is None:
fp = open('default.txt')
close_fp = True
try:
return fp.read(100)
finally:
if close_fp:
fp.close()
为了使它更“漂亮”,考虑改变接口,这样你就不必同时处理读取数据和从同一个函数中进行资源管理 - 重构使你的函数只有一个责任。
答案 2 :(得分:3)
您可以定义一个自定义上下文管理器,只有在传递给它时才会执行某些操作,但它可能有点过分:
class ContextOrNone(object):
def __init__(self, obj, fn, *args, **kwargs):
if obj is not None:
self.obj = obj
self.cleanup = False
else:
self.obj = fn(*args, **kwargs)
self.cleanup = True
def __enter__(self):
return self.obj
def __exit__(self, ex_type, ex_val, traceback):
if self.cleanup:
self.obj.__exit__(ex_type, ex_val, traceback)
或者,使用contextlib.contextmanager
:
from contextlib import contextmanager
@contextmanager
def ContextOrNone(obj, fn, *args, **kwargs):
was_none = obj is None
try:
if was_none:
obj = fn(*args, **kwargs)
yield obj
finally:
if was_none:
obj.__exit__()
定义完成后,您可以将readSomething
定义为:
def readSomething(fp=None):
with ContextOrNone(fp, open, 'default.txt', 'r') as fp:
return fp.read(100)
答案 3 :(得分:2)
老实说,你的代码中最Pythonic版本可能就是你已经拥有的版本,除了稍微清理一下:
def readSomething(fp=None):
if fp:
return fp.read(100)
with open('default.txt') as fp:
return fp.read(100)
保留您原有的意图和功能。它清晰易读。当然,它有点重复。如果你的例子被简化到重复部分对你来说太怪异了,那么把它提升到自己的功能中:
def complicatedStuff(buf, sz):
# Obviously more code will go here.
return buf.read(sz)
def readSomething(fp=None):
if fp:
return complicatedStuff(fp, 100)
with open('default.txt') as fp:
return complicatedStuff(fp, 100)
为了避免重复自己一点点,不要让Pythonic跳过很多箍。
答案 4 :(得分:0)
这不使用with
,并关闭默认值或作为参数传递的文件,
但也许它仍然是一种选择。
def readSomething(fp=None):
if fp is None:
fp = open('default.txt')
return (fp.read(100), fp.close)