【问题标题】:Should an «interface» to my class constructor be part of my class? (How?)我的类构造函数的“接口”应该是我的类的一部分吗? (如何?)
【发布时间】:2019-03-29 00:43:26
【问题描述】:

我有一个带有非常通用的初始化器的类,它接受argskwargs 的任意组合。我想实现另一种构建实例的方法,它将使用 3 个列表:一个用于args,两个用于kwargsKeyvalues,长度相同)。显然,我不能用我的普通 __init()__ 方法做到这一点,因为传递 3 个列表是一个完全有效的案例。

class MyVeryGenericClass:
    __init__(self, *args, **kwargs):
        pass   #Do something really nice with args and kwargs, any case is valid.

出于这个原因,我想,我将不得不使用另一个函数来环绕我的 __init__ 函数,如下所示:

def mvgc_wrapper(lll, keys, vals):
    if len(keys) != len(vals):
        raise ValueError("keys and vals must have the same length.")
    return MyVeryGenericClass(*lll, **dict(zip(keys, vals)))

问题是:我的包装函数是否应该是我的类的方法,出于什么原因,如果是,那么如何?

  • 一方面,除了__new____init__ 等特殊方法外,类实例应该已经存在,但事实并非如此。
  • 另一方面,让我的包装器成为我的类的一部分以使其成为整体会更合乎逻辑。
  • 否则,我是否应该以与__new__ 相同的方式定义它,使用 cls 参数而不是 self。我试着写成这样,但它似乎不起作用。

代码:

#!/usr/bin/python3
# coding: UTF-8

import traceback

class MyVeryGenericClass:
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

    def __repr__(self):
        return repr(self.args)+repr(self.kwargs)

    def wrapper(cls, lll, keys, vals):
        if len(keys) != len(vals):
            raise ValueError("keys and vals must have the same length.")
        return MyVeryGenericClass(*lll, **dict(zip(keys, vals)))

    def wrapper2(cls, lll, keys, vals):
        if len(keys) != len(vals):
            raise ValueError("keys and vals must have the same length.")
        return cls(*lll, **dict(zip(keys, vals)))

    def wrapper3(cls, lll, keys, vals):
        if len(keys) != len(vals):
            raise ValueError("keys and vals must have the same length.")
        return cls.MyVeryGenericClass(*lll, **dict(zip(keys, vals)))


genlist = [1, 2, 3]
genkeys = ["spam", "eggs"]
genvals = ["foo", "bar"]

try:
    cmd="instance1 = MyVeryGenericClass(genlist, genkeys, genvals)"
    print(">", cmd)
    exec(cmd)
    print("instance1 = {}".format(repr(instance1)))
except TypeError: traceback.print_exc()
print()
try:
    cmd="instance2 = instance1.wrapper(genlist, genkeys, genvals)"
    print(">", cmd)
    exec(cmd)
    print("instance2 = {}".format(repr(instance2)))
except TypeError: traceback.print_exc()
print()
try:
    cmd="failed_instance = MyVeryGenericClass.wrapper(genlist, genkeys, genvals)"
    print(">", cmd)
    exec(cmd)
    print("failed_instance = {}".format(repr(failed_instance)))
except TypeError: traceback.print_exc()
print()
try:
    cmd="failed_instance = MyVeryGenericClass.wrapper2(genlist, genkeys, genvals)"
    print(">", cmd)
    exec(cmd)
    print("failed_instance = {}".format(repr(failed_instance)))
except TypeError: traceback.print_exc()
print()
try:
    cmd="failed_instance = MyVeryGenericClass.wrapper3(genlist, genkeys, genvals)"
    print(">", cmd)
    exec(cmd)
    print("failed_instance = {}".format(repr(failed_instance)))
except TypeError: traceback.print_exc()

生产:

RESTART: /media/raid/ArcFolder/Mes documents/Mes Textes/Mes programmes/python/init_wrapper.py
> instance1 = MyVeryGenericClass(genlist, genkeys, genvals)
instance1 = ([1, 2, 3], ['spam', 'eggs'], ['foo', 'bar']){}

> instance2 = instance1.wrapper(genlist, genkeys, genvals)
instance2 = (1, 2, 3){'eggs': 'bar', 'spam': 'foo'}

> failed_instance = MyVeryGenericClass.wrapper(genlist, genkeys, genvals)
Traceback (most recent call last):
  File "/media/raid/ArcFolder/Mes documents/Mes Textes/Mes programmes/python/init_wrapper.py", line 51, in <module>
    exec(cmd)
  File "<string>", line 1, in <module>
TypeError: wrapper() missing 1 required positional argument: 'vals'

> failed_instance = MyVeryGenericClass.wrapper2(genlist, genkeys, genvals)
Traceback (most recent call last):
  File "/media/raid/ArcFolder/Mes documents/Mes Textes/Mes programmes/python/init_wrapper.py", line 58, in <module>
    exec(cmd)
  File "<string>", line 1, in <module>
TypeError: wrapper2() missing 1 required positional argument: 'vals'

> failed_instance = MyVeryGenericClass.wrapper3(genlist, genkeys, genvals)
Traceback (most recent call last):
  File "/media/raid/ArcFolder/Mes documents/Mes Textes/Mes programmes/python/init_wrapper.py", line 65, in <module>
    exec(cmd)
  File "<string>", line 1, in <module>
TypeError: wrapper3() missing 1 required positional argument: 'vals'

编辑: 感谢@kindall,解决方案是在备用构造函数上方添加#classmethod。但现在的问题是:如何区分备用构造函数和备用初始化器?

【问题讨论】:

  • 备用构造函数通常使用@classmethod
  • 这不是一个接口顺便说一句,我不明白你为什么不将 3 个列表传递给构造函数而不是这样做
  • 因为,我的 init 方法无法检测到两个输入之间的差异,并且会产生错误的结果(请参阅我的实例 1 和实例 2 的内容之间的差异最后一个例子)(关于接口,这就是我引用它的原因。)
  • 所以你无法控制类的代码,你需要在两者之间添加一些东西来动态修改参数?
  • 我可以控制类的代码,但我不想失去我的正常初始化程序。

标签: python python-3.x class constructor


【解决方案1】:

已解决,感谢@kindall。我需要在我的备用初始化器上方添加@classmethod

#!/usr/bin/python3
# coding: UTF-8

import traceback

class MyVeryGenericClass:
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

    def __repr__(self):
        return repr(self.args)+repr(self.kwargs)

    @classmethod
    def wrapper(cls, lll, keys, vals):
        if len(keys) != len(vals):
            raise ValueError("keys and vals must have the same length.")
        return MyVeryGenericClass(*lll, **dict(zip(keys, vals)))

    @classmethod
    def wrapper2(cls, lll, keys, vals):
        if len(keys) != len(vals):
            raise ValueError("keys and vals must have the same length.")
        return cls(*lll, **dict(zip(keys, vals)))


genlist = [1, 2, 3]
genkeys = ["spam", "eggs"]
genvals = ["foo", "bar"]

try:
    cmd="instance1 = MyVeryGenericClass(genlist, genkeys, genvals)"
    print(">", cmd)
    exec(cmd)
    print("instance1 = {}".format(repr(instance1)))
except TypeError: traceback.print_exc()
print()
try:
    cmd="instance2 = instance1.wrapper(genlist, genkeys, genvals)"
    print(">", cmd)
    exec(cmd)
    print("instance2 = {}".format(repr(instance2)))
except TypeError: traceback.print_exc()
print()
try:
    cmd="failed_instance = MyVeryGenericClass.wrapper(genlist, genkeys, genvals)"
    print(">", cmd)
    exec(cmd)
    print("failed_instance = {}".format(repr(failed_instance)))
except TypeError: traceback.print_exc()
print()
try:
    cmd="failed_instance = MyVeryGenericClass.wrapper2(genlist, genkeys, genvals)"
    print(">", cmd)
    exec(cmd)
    print("failed_instance = {}".format(repr(failed_instance)))
except TypeError: traceback.print_exc()

生产:

 RESTART: /media/raid/ArcFolder/Mes documents/Mes Textes/Mes programmes/python/init_wrapper.py 
> instance1 = MyVeryGenericClass(genlist, genkeys, genvals)
instance1 = ([1, 2, 3], ['spam', 'eggs'], ['foo', 'bar']){}

> instance2 = instance1.wrapper(genlist, genkeys, genvals)
instance2 = (1, 2, 3){'spam': 'foo', 'eggs': 'bar'}

> instance3 = MyVeryGenericClass.wrapper(genlist, genkeys, genvals)
instance3 = (1, 2, 3){'spam': 'foo', 'eggs': 'bar'}

> instance4 = MyVeryGenericClass.wrapper2(genlist, genkeys, genvals)
instance4 = (1, 2, 3){'spam': 'foo', 'eggs': 'bar'}

【讨论】:

    【解决方案2】:

    如果您可以控制类代码,那么您可以这样做:

    class A:
        def __init__(self, args, keys, values):
            self.args = args
            self.kwargs = dict(zip(keys, values))
    
        def __repr__(self):
            return repr(self.args) + repr(self.kwargs)
    

    你可以使用如你所展示的:

    >>> genlist = [1, 2, 3]
    >>> genkeys = ["spam", "eggs"]
    >>> genvals = ["foo", "bar"]
    >>> a = A(genlist, genkeys, genvals)
    >>> print(a)
    [1, 2, 3]{'spam': 'foo', 'eggs': 'bar'}
    

    除非有充分的理由不这样做,否则最好的办法可能是将您的键/值对直接作为关键字参数直接传递:

    class A:
        def __init__(self, *args, **kwargs):
            pass
    
    a = A(genlist, **dict(zip(genkeys, genvals)))
    

    【讨论】:

    • 但是,我没有我的主初始化器了。
    • 如果您不将它用于其他任何事情,那么拥有它没有任何意义,也许您应该详细说明您最终需要拥有的东西(您的实际目标)。
    • 这并不是说我不将它用于其他任何事情。只是我使示例尽可能短。我的主要用途是能够用 *arg 和 **kwargs 调用它,(我想制作一个通用模块,它将在许多不同的情况下使用,所以没有必要详细说明我还不知道的情况)。关键是在生成器中使用时,传递 3 个列表而不是可变参数范式更容易。
    • 我相信你忘记了 genlist 之前的«*»。顺便说一句,你上一版是我首先做的,但得出的结论是,这种构建对象的另一种方式足够通用,可以放在我的类模块中,这就是为什么我想知道应该怎么做.
    • 哦,顺便说一句,看我的代码让我想起了为什么需要备用构造函数:这是因为如果 genkeys 和 genvals 的长度不同,zip 不会引发错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 2012-05-25
    • 2021-05-05
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多