python - 有什么方法可以绕过 namedtuple 255 个参数限制?

标签 python namedtuple

我正在使用 namedtuple 来保存字符串集及其对应的值。 我没有使用字典,因为我希望字符串可以作为属性访问。

这是我的代码:

from collections import namedtuple


# Shortened for readability :-)
strings = namedtuple("strings", ['a0', 'a1', 'a2', ..., 'a400'])
my_strings = strings(value0, value1, value2, ..., value400)

理想情况下,一旦 my_strings 被初始化,我应该能够这样做:

print(my_strings.a1) 

并打印回 value1

但是,我收到以下错误:

strings(value0, value1, value2, ...value400)

   ^SyntaxError: more than 255 arguments

似乎 python 函数(包括 namedtuple 的 init())在调用时不接受超过 255 个参数。 有什么方法可以绕过这个问题并命名包含超过 255 个项目的元组吗?为什么有 255 个参数的限制?

最佳答案

这是对 CPython 函数定义的限制;在 Python 3.7 之前的版本中,您不能为可调用对象指定超过 255 个显式参数。这适用于任何函数定义,而不仅仅是命名元组。

请注意,此限制已在 Python 3.7 及更新版本中取消,其中新限制为 sys.maxint。参见 What is a maximum number of arguments in a Python function?

这是达到此限制的类的生成代码。您不能定义一个参数超过 255 个的函数;因此,结果类的 __new__ 类方法在 CPython 实现中无法实现。

但是,您必须问问自己,您是否真的应该使用不同的结构。看起来你对我来说有一个类似列表的数据; 400 个带编号的名字是您 data bleeding into your names 的确定标志.

您可以通过手动创建自己的子类来解决此问题:

from operator import itemgetter
from collections import OrderedDict

class strings(tuple):
    __slots__ = ()
    _fields = tuple('a{}'.format(i) for i in range(400))
    def __new__(cls, *args, **kwargs):
        req = len(cls._fields)
        if len(args) + len(kwargs) > req:
            raise TypeError(
                '__new__() takes {} positional arguments but {} were given'.format(
                    req, len(args) + len(kwargs)))
        if kwargs.keys() > set(cls._fields):
            raise TypeError(
                '__new__() got an unexpected keyword argument {!r}'.format(
                    (kwargs.keys() - set(cls._fields)).pop()))
        missing = req - len(args)
        if kwargs.keys() & set(cls._fields[:-missing]):
            raise TypeError(
                '__new__() got multiple values for argument {!r}'.format(
                    (kwargs.keys() & set(cls._fields[:-missing])).pop()))
        try:
            for field in cls._fields[-missing:]:
                args += (kwargs[field],)
                missing -= 1
        except KeyError:
            pass
        if len(args) < req:
            raise TypeError('__new__() missing {} positional argument{}: {}'.format(
                missing, 's' if missing > 1 else '',
                ' and '.join(filter(None, [', '.join(map(repr, cls._fields[-missing:-1])), repr(cls._fields[-1])]))))
        return tuple.__new__(cls, args)

    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        'Make a new strings object from a sequence or iterable'
        result = new(cls, iterable)
        if len(result) != len(cls._fields):
            raise TypeError('Expected %d arguments, got %d' % (len(cls._fields), len(result)))
        return result

    def __repr__(self):
        'Return a nicely formatted representation string'
        format = '{}({})'.format(self.__class__.__name__, ', '.join('{}=%r'.format(n) for n in self._fields))
        return format % self

    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values'
        return OrderedDict(zip(self._fields, self))

    __dict__ = property(_asdict)

    def _replace(self, **kwds):
        'Return a new strings object replacing specified fields with new values'
        result = self._make(map(kwds.pop, self._fields, self))
        if kwds:
            raise ValueError('Got unexpected field names: %r' % list(kwds))
        return result

    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self)

    def __getstate__(self):
        'Exclude the OrderedDict from pickling'
        return None

for i, name in enumerate(strings._fields):
    setattr(strings, name, 
            property(itemgetter(i), doc='Alias for field number {}'.format(i)))

这个版本的命名元组完全避免了长参数列表,但在其他方面与原始版本完全一样。有点冗长的 __new__ 方法并不是严格需要的,但在参数不完整时确实会模拟原始行为。注意 _fields 属性的构造;将其替换为您自己的名称以命名您的元组字段。

传入一个生成器表达式来设置你的参数:

s = strings(i for i in range(400))

或者如果您有一个值列表:

s = strings(iter(list_of_values))

这两种技术都绕过了函数签名和函数调用参数计数的限制。

演示:

>>> s = strings(i for i in range(400))
>>> s
strings(a0=0, a1=1, a2=2, a3=3, a4=4, a5=5, a6=6, a7=7, a8=8, a9=9, a10=10, a11=11, a12=12, a13=13, a14=14, a15=15, a16=16, a17=17, a18=18, a19=19, a20=20, a21=21, a22=22, a23=23, a24=24, a25=25, a26=26, a27=27, a28=28, a29=29, a30=30, a31=31, a32=32, a33=33, a34=34, a35=35, a36=36, a37=37, a38=38, a39=39, a40=40, a41=41, a42=42, a43=43, a44=44, a45=45, a46=46, a47=47, a48=48, a49=49, a50=50, a51=51, a52=52, a53=53, a54=54, a55=55, a56=56, a57=57, a58=58, a59=59, a60=60, a61=61, a62=62, a63=63, a64=64, a65=65, a66=66, a67=67, a68=68, a69=69, a70=70, a71=71, a72=72, a73=73, a74=74, a75=75, a76=76, a77=77, a78=78, a79=79, a80=80, a81=81, a82=82, a83=83, a84=84, a85=85, a86=86, a87=87, a88=88, a89=89, a90=90, a91=91, a92=92, a93=93, a94=94, a95=95, a96=96, a97=97, a98=98, a99=99, a100=100, a101=101, a102=102, a103=103, a104=104, a105=105, a106=106, a107=107, a108=108, a109=109, a110=110, a111=111, a112=112, a113=113, a114=114, a115=115, a116=116, a117=117, a118=118, a119=119, a120=120, a121=121, a122=122, a123=123, a124=124, a125=125, a126=126, a127=127, a128=128, a129=129, a130=130, a131=131, a132=132, a133=133, a134=134, a135=135, a136=136, a137=137, a138=138, a139=139, a140=140, a141=141, a142=142, a143=143, a144=144, a145=145, a146=146, a147=147, a148=148, a149=149, a150=150, a151=151, a152=152, a153=153, a154=154, a155=155, a156=156, a157=157, a158=158, a159=159, a160=160, a161=161, a162=162, a163=163, a164=164, a165=165, a166=166, a167=167, a168=168, a169=169, a170=170, a171=171, a172=172, a173=173, a174=174, a175=175, a176=176, a177=177, a178=178, a179=179, a180=180, a181=181, a182=182, a183=183, a184=184, a185=185, a186=186, a187=187, a188=188, a189=189, a190=190, a191=191, a192=192, a193=193, a194=194, a195=195, a196=196, a197=197, a198=198, a199=199, a200=200, a201=201, a202=202, a203=203, a204=204, a205=205, a206=206, a207=207, a208=208, a209=209, a210=210, a211=211, a212=212, a213=213, a214=214, a215=215, a216=216, a217=217, a218=218, a219=219, a220=220, a221=221, a222=222, a223=223, a224=224, a225=225, a226=226, a227=227, a228=228, a229=229, a230=230, a231=231, a232=232, a233=233, a234=234, a235=235, a236=236, a237=237, a238=238, a239=239, a240=240, a241=241, a242=242, a243=243, a244=244, a245=245, a246=246, a247=247, a248=248, a249=249, a250=250, a251=251, a252=252, a253=253, a254=254, a255=255, a256=256, a257=257, a258=258, a259=259, a260=260, a261=261, a262=262, a263=263, a264=264, a265=265, a266=266, a267=267, a268=268, a269=269, a270=270, a271=271, a272=272, a273=273, a274=274, a275=275, a276=276, a277=277, a278=278, a279=279, a280=280, a281=281, a282=282, a283=283, a284=284, a285=285, a286=286, a287=287, a288=288, a289=289, a290=290, a291=291, a292=292, a293=293, a294=294, a295=295, a296=296, a297=297, a298=298, a299=299, a300=300, a301=301, a302=302, a303=303, a304=304, a305=305, a306=306, a307=307, a308=308, a309=309, a310=310, a311=311, a312=312, a313=313, a314=314, a315=315, a316=316, a317=317, a318=318, a319=319, a320=320, a321=321, a322=322, a323=323, a324=324, a325=325, a326=326, a327=327, a328=328, a329=329, a330=330, a331=331, a332=332, a333=333, a334=334, a335=335, a336=336, a337=337, a338=338, a339=339, a340=340, a341=341, a342=342, a343=343, a344=344, a345=345, a346=346, a347=347, a348=348, a349=349, a350=350, a351=351, a352=352, a353=353, a354=354, a355=355, a356=356, a357=357, a358=358, a359=359, a360=360, a361=361, a362=362, a363=363, a364=364, a365=365, a366=366, a367=367, a368=368, a369=369, a370=370, a371=371, a372=372, a373=373, a374=374, a375=375, a376=376, a377=377, a378=378, a379=379, a380=380, a381=381, a382=382, a383=383, a384=384, a385=385, a386=386, a387=387, a388=388, a389=389, a390=390, a391=391, a392=392, a393=393, a394=394, a395=395, a396=396, a397=397, a398=398, a399=399)
>>> s.a391
391

关于python - 有什么方法可以绕过 namedtuple 255 个参数限制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18550270/

相关文章:

python - 将所有对象中的一个 QuerySet 元素从 int 转换为 Decimal

python - 1366 mysql注音付号的字符串值不正确

python - 使用 python 多处理模块缓冲/截断到文件的 Shell 脚本输出

python - Django 应用程序 View - 如何从 url 中的 django 数据库获取条目并从数据库获取 View 的更多信息?

java - 在没有 SPSS 许可证的情况下从 Python、Java (groovy/grails) 或 C++ 应用程序访问 SPSS 数据?

python - 如何在多重继承中使用命名元组

python - `namedtuple` 在 3.5.1 中有什么变化吗?

python - 从父类访问命名元组中的派生属性

python - 命名元组中类型名的相关性

python - NamedTuple 到 Dataframe