Должен ли «интерфейс» моего конструктора класса быть частью моего класса?(Как?) - PullRequest
0 голосов
/ 29 марта 2019

У меня есть класс с очень общим инициализатором, принимающим любую комбинацию args и kwargs. Я хотел бы реализовать другой способ создания экземпляров, который будет использовать 3 списка: один для args и два для kwargs (Key и values, оба одинаковой длины). Очевидно, что я не могу сделать это с помощью моего обычного __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 над альтернативным конструктором. Но теперь проблема в следующем: как мне отличить альтернативный конструктор от альтернативного инициализатора?

Ответы [ 2 ]

0 голосов
/ 29 марта 2019

Решено, спасибо @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'}
0 голосов
/ 29 марта 2019

Если у вас есть контроль над кодом класса, то вы можете просто сделать это:

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)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...