Python如何优雅地处理不同数据结构的用法?

问题描述:

我使用Python 3.5,这是我处理目前代码:Python如何优雅地处理不同数据结构的用法?

def is_odd_number(n): 
    """states if n is an odd number""" 
    return n % 2 == 1 


def collatz_next(n): 
    """returns the successor to n in Collatz sequence""" 
    return 3*n + 1 if is_odd_number(n) else n//2 


def collatz_seq_sum(seq): 
    """returns the sum of all elements to a given Collatz sequence""" 
    return sum(seq) 


def collatz_seq(n): 
    """returns the Collatz sequence to n""" 
    l = [] 
    l.append(n) 
    current = n 
    while current != 1: 
     next_one = collatz_next(current) 
     l.append(next_one) 
     current = next_one 
    return l 


def collatz_seqs(lower_limit=1, upper_limit=10): 
    """returns Collatz sequences from lower_limit to upper_limit""" 
    return {i: collatz_seq(i) for i in range(lower_limit, upper_limit+1)} 

我认为这类型list是最好的一个在Collat​​z序列的时候。这就是为什么collatz_seq返回一个列表。然而,我发现观察连续处理多个参数n时这个特定序列是如何发展是很有趣的。这就是为什么我创建了collatz_seqs

我不喜欢collatz_seq_sum有一个非常简单的原因:它只有在参数seq的类型为list时才能正常工作。在我看来,collatz_seq_sum不是确保提供适当的实际参数的责任,在这种情况下,list由自然数组成。在我看来,致电collatz_seq_sum必须确保它提供了一个正确的参数。

我想要collatz_sum_seq可以使用单个和多个序列。 collatz_seq返回listcollatz_seqs返回dict。因此我的问题是:确保collatz_seq_sum始终为其参数seq获取正确数据类型的优雅方法是什么?关于collatz_seq_sum的正确工作,我可以做些什么?collatz_seq_sum关心其参数seq的数据类型?我的第一个想法是更改collatz_seq以使其返回dict而不是list并更改collatz_seq_sum以处理dict。但是,我不喜欢这种方法,因为我不想在处理单个序列时使用dict

您有任何解决方案吗?非常感谢你提前。

+0

[mypy](https://github.com/python/mypy)静态类型检查库提供了[装饰器](https://jeffknupp.com/blog/2013/11/29/improve-your- python-decorators-explained /)命名为“重载”,它似乎允许你想要的那种多态:http://mypy.readthedocs.io/en/latest/function_overloading.html。看起来很有希望给我。 –

+0

会[functools.singledispatch](https://docs.python.org/3/library/functools.html#functools.singledispatch)有帮助吗? – wwii

是检查给出的参数的类型:

from collections.abc import Mapping, Iterable 

def collatz_seq_sum(seq): 
    """returns the sum of all elements to a given Collatz sequence""" 
    if isinstance(seq, Mapping): 
     ret = {key: sum(values) for key, values in seq.items()} 
    elif isinstance(seq, Iterable): 
     ret = sum(seq) 
    else: 
     msg = "collatz_seq_sum got unexpected type: '{}'".format(type(seq)) 
     raise TypeError(msg) 
    return ret 
如果你想要一个功能不同的行为取决于输入这种类型

是要走的路。

+0

感谢您的输入。这是一个很好的论据,我理解它。我只是想知道:这是唯一的方法吗?假设您使用的脚本使用了数百个(如果不是数千个)函数。我的意思是它总是一遍又一遍:每个函数检查其参数的类型并相应地执行操作。一遍又一遍地做同样的事情变得非常乏味,我认为很难阅读和维护。 – 6q9nqBjo

+0

这是什么类(和鸭子打字)。这是Python的哲学。 –

如果我理解正确,那么您希望collatz_seq_sum可以同时使用collat​​z序列或单个collat​​z序列。在字典的情况下,您希望函数返回单个拼贴序列总和的字典。

您可以使用isinstance来检查输入seq是字典还是列表,并针对每种情况运行不同的代码。下面的代码将工作。

def collatz_seq_sum(seq): 
    """returns the sum of all elements to a given Collatz sequence""" 
    if isinstance(seq, dict): 
     return {i: sum(seqi) for i, seqi in seq.items()} 
    else: 
     return sum(seq) 

但是,如果你想要的是什么字典的所有序列的总和,你可以使用,而不是做多态的功能在Python的唯一途径以下,

def collatz_seq_sum(seq): 
    """returns the sum of all elements to a given Collatz sequence""" 
    if isinstance(seq, dict): 
     return sum([sum(seqi) for i, seqi in seq.items()]) 
    else: 
     return sum(seq) 
+0

感谢您的意见。那么,不完全是。我不希望'collat​​z_seq_sum'根本不打算使用'seq'的数据类型,因为我不认为'collat​​z_seq_sum'的作用是检查数据类型。计算金额是它的工作;没有其他的。 – 6q9nqBjo

+0

你的意思是说你想让collat​​z_seq_sum在任何数据类型上工作,而不检查数据类型吗? –

+0

是的,这是基本的想法,因为我不想在处理数百个函数时做几乎相同的检查。 – 6q9nqBjo

Python 3.4+ - functools.singledispatch让你过载基于参数类型的函数定义。

from functools import singledispatch 

@singledispatch 
def collatz_seq_sum(seq): 
    '''returns the sum of all elements to a given Collatz sequence''' 
    raise NotImplementedError("I can't handle that data type") 

@collatz_seq_sum.register(list) 
def _(seq): 
    return sum(seq) 

@collatz_seq_sum.register(dict) 
def _(seq): 
    return {key: sum(values) for key, values in seq.items()} 

>>> collatz_seq_sum([1,2,3,4,5]) 
15 
>>> collatz_seq_sum({'a': [1,1,1,1], 'b': [2,2,2,2]}) 
{'a': 4, 'b': 8} 

调用collatz_seq_sum比列表或字典其他东西将引发NotImplementedError

+0

感谢您的输入。这也是一个非常有趣的方法。你使用了“超载”这个词。你的代码是Python的装饰器功能的应用程序吗? @标志让我想起了它。 – 6q9nqBjo

+0

@ 6q9nqBjo,我从它的文档中取出了超载这个词。这是一个装饰者 - 我没有花时间去看看它是如何实现的。在我的答案中有一个链接到文档。 – wwii