1

需要意见。

我有一个定义一些数据的函数。我的想法是用户可以告诉它从文件中读取数据:

acquire_data('read_from_file',filename)

或者用户可以直接提供数据:

acquire_data('use_this_list',datalist)

所以这个函数会有一个类似的形式

def acquire_data(mode,arg2):
    if mode == 'read_from_file':
        inputs=open(arg2)
        data = #etc.
    else:
        data = arg2  #or deepcopy(arg2) or whatever

嗯,这行得通,但它似乎有点陈旧。特别是,“arg2”根据“mode”的值具有非常不同的功能。所以:这是好代码吗?这是“pythonic”吗?有人看到更好的编码方法吗?谢谢。

4

6 回答 6

5
def acquire_data(list_or_filename):
    # assuming py3 here, for py2 use 'isinstance(list_or_filename, basestring)
    if isinstance(list_or_filename, str):
        with open(list_or_filename,"r") as f:
            return acquire_data_from_file(f)
    else:
        return acquire_data_from_list(list_or_filename)
于 2013-02-27T18:52:01.857 回答
4

与其让acquire_data打开并读取文件的全部内容,不如将 a 传递fileObjacquire_data.

这是一个更好的设计的原因

  1. 序列、生成器和文件的统一接口
  2. 您不需要读取函数中的全部数据
  3. 即使发生异常,您也可以控制文件的生命周期

骨架代码将是

def acquire_data(iterable):
    for line in iterable:
        # Do what ever you want

with open("whatever") as fin:
    acquire_data(fin)

acquire_data(some_seq)

acquire_data(some_gen)
于 2013-02-27T18:56:04.853 回答
2

只为不同的任务使用不同的函数怎么样:acquire_data_from_file, acquire_data_from_list, 等等?更加清晰和简单。

于 2013-02-27T18:56:20.620 回答
1

如果你真的想要一个可以接受文件名或数据列表的函数(你可能不这样做,但你似乎对 oseiskar 和 Abhijit 建议的更多 Pythonic 替代方案有抵抗力),你绝对不想这样做方法。

一般来说,如果你发现自己在进行类型切换,那么你做错了什么。但是基于字符串做假类型切换,依赖用户匹配字符串和类型,就更不对了。

一种替代方法是尝试打开文件名,如果失败,则假定它是一个序列而不是文件名。(请求宽恕比请求许可更容易。)例如:

def acquire_data(filename_or_list):
    try:
        with open(filename_or_list) as f:
            data = list(f)
    except TypeError:
        data = list(filename_or_list)
    # ... now the rest of your code uses data

即使用户传递一个unicode文件名而不是一个str,或者一个tuple而不是一个list,或者甚至是你从未听说过的某些类,它也适用于openorlist函数。这就是鸭式打字的精髓。

当然,这有点滥用鸭子类型,但这是您的问题所固有的:您正在尝试采用两种不同类型之一的参数,并且任何使其起作用的解决方案都将滥用某些功能。

唯一的担心是有人可能会传递一些同时适用于open和的东西list。事实上,对于普通的旧str. 所以你需要一个通用的决定来处理这种情况——似乎首先尝试将其作为路径名,然后作为序列,比反之更好。这当然是你想要的str,但你必须考虑这是否适用于双向工作的每一种可能的类型。

于 2013-02-27T19:10:43.290 回答
1

这种工作我会进一步划分并使用 dict 作为 switch case :

def read_file(arg):
 # code

def read_data(arg):
 # code

def default_f(arg):
 # code

def acquire_data(mode, arg2):

    fun = {
    'read_from_file': read_file, 
    'use_this_list': read_data
    }.get(mode, default_f)

    fun(arg2)

编辑
第二种方法:结合我和@möter。

def acquire_data(arg):
   fun = {
     True: read_file,
     False: read_data
     }.get(
         isinstance(arg, str),
         default_f
         )
   return fun(arg);

称之为:

acquire_data('read_from_file',filename)
acquire_data('use_this_list',datalist)
于 2013-02-27T18:55:06.977 回答
1

由于 Python 的鸭子类型,让函数根据变量或参数的数据类型执行不同的行为,这种做法相对常见。

你可以让你的功能保持原样,虽然我会考虑这样做

if mode == 'read_from_file':
        inputs=open(arg2)
        data = #etc.
elif mode == 'use_this_list':
        data = arg2  #or deepcopy(arg2) or whatever
else:
        raise InputError # or something like this

只是为了确保您的函数是可扩展的,并且您没有将任何不正确的参数传递给函数的第二部分。

除了数据本身之外,另一种方法可能是简单地接受打开的文件和/或文件名:

def acquire_data(arg):
    if isinstance(arg, file):
        data = arg.read() # make sure to parse the data
    elif isinstance(arg, basestring):
        data = open(arg, 'r').read() # make sure to parse
    else:
        data = arg
于 2013-02-27T19:00:33.363 回答