需要关于用类创建对象的帮助吗

2024-03-28 09:25:39 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要帮助创建一个对象(一个数字序列)关于一个类的一些参数。假设我输入了Python空闲shell:

SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
   #means user can create a 'SuperLotto' with 6 numbers in range of 1 to 50

它将使“超级乐透”成为名为“LotteryGameType”的类的新类实例。你知道吗

到目前为止,这是在使用代码:

class LotterySetError(Exception):
     pass               

def make_lottery_set_type(name:str, size:int, minmax:tuple):
    if minmax[0] > minmax[1]:
        raise LotterySetError('Illegal range for tuple')
    else:
        name = LotteryGameType(name, size, minmax[0], minmax[1])
    return name

class LotteryGameType:
    def __init__(self, name, set_size, min_set_number, max_set_number):
        self.name = name
        self.set_size = set_size
        self.min_set_number = min_set_number
        self.max_set_number = max_set_number

我希望能够创建一个数字序列并将其存储起来以备以后使用,这样我就可以将其与重载运算符(例如eqne)一起使用。你知道吗

我希望能够在Python空闲shell中键入:

SuperLotto([3, 4, 19, 23, 46, 27])

这将在SuperLotto的参数下创建一个对象,如果不是在“SuperLotto”的参数下(比如超过6个数字),则会产生错误。任何方法都可以。有人对如何处理这个问题有什么想法吗?你知道吗


Tags: 对象nameselfnumber参数size序列数字
1条回答
网友
1楼 · 发布于 2024-03-28 09:25:39

听起来您想要的是make_lottery_set_type返回一个新的class,可能是LotteryGameType的子类,而不是返回该类型的实例。你知道吗

在Python中,这实际上很容易做到。类定义只是普通的代码,可以在任何地方运行,甚至在函数的中间。它们在运行时可以访问本地环境。类本身是“第一类值”,这意味着您可以传递它们并从函数返回它们。所以:

def make_lottery_set_type(name:str, size:int, minmax:tuple):
    if minmax[0] > minmax[1]:
        raise LotterySetError('Illegal range for tuple')
    else:
        class NewLotteryGameType(LotteryGameType):
            def __init__(self, numbers):
                super().__init__(name, size, minmax[0], minmax[1])
                self.numbers = numbers
        return NewLotteryGameType

如果要添加其他方法,这与向任何其他类添加方法相同。例如:

def make_lottery_set_type(name:str, size:int, minmax:tuple):
    if minmax[0] > minmax[1]:
        raise LotterySetError('Illegal range for tuple')
    else:
        class NewLotteryGameType(LotteryGameType):
            def __init__(self, numbers):
                super().__init__(name, size, minmax[0], minmax[1])
                self.numbers = numbers
            def __eq__(self, rhs):
                return set(self.numbers) == set(rhs.numbers)
        return NewLotteryGameType

所以:

>>> SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
>>> super1 = SuperLotto([1,2,3,4,5,6])
>>> super2 = SuperLotto([6,5,4,3,2,1])
>>> super3 = SuperLotto([7,8,9,10,11,12])
>>> super1 == super2
True
>>> super1 == super3
False

(显然,如果set equality不是适合您使用的规则,您可以根据需要定义__eq__


如果您尝试检查生成的值,它们看起来并不像您希望的那么漂亮。例如,您可能更愿意在这样的地方看到SuperLotto而不是NewLotteryGameType

>>> super1
<__main__.NewLotteryGameType at 0x10259e490>
>>> SuperLotto.__name__
'NewLotteryGameType'

为此,只需添加NewLotteryGameType.__name__ = name。您可能还想从父类复制docstring或其他各种内容。你知道吗

一般来说,请看functools.update_wrapper(它是为包装函数而设计的,而不是类,但许多细节是相同的)以获得灵感,并查看Python版本中的inspect模块文档以了解类可以拥有的所有属性。你知道吗


在评论中,你会问:

The only problem is that I want NewLotteryGameType to inherit the parameters such as name, set_size, min_set_number, max_set_number from LotteryGameType. So lets say I wanted to type in NewLotteryGameType.set_size in to the Python Shell. I want it to return back to me 6.

这是自相矛盾的。如果您想继承LotteryGameType的实例属性,那么您已经继承了。例如:

>>> super1.set_size
6

如果您想让它们在类外可以访问,那么它们不能是实例属性,必须是类属性。仅仅将set_size更改为LotteryGameType的class属性并继承它是行不通的,因为class属性的关键是类的所有实例或其任何子类共享相同的值,子类都需要不同的值。你知道吗

但你可以这样做:

class LotteryGameType:
    def __init__(self, min_set_number, max_set_number):
        self.min_set_number = min_set_number
        self.max_set_number = max_set_number

def make_lottery_set_type(lottery_name:str, size:int, minmax:tuple):
    if minmax[0] > minmax[1]:
        raise LotterySetError('Illegal range for tuple')
    else:
        class NewLotteryGameType(LotteryGameType):
            name = lottery_name
            set_size = size
            def __init__(self, numbers):
                super().__init__(minmax[0], minmax[1])
                self.numbers = numbers
            def __eq__(self, rhs):
                return set(self.numbers) == set(rhs.numbers)
        return NewLotteryGameType

(注意,由于作用域的工作方式,我不得不将第一个make_参数重命名为lottery_name,因此它与类属性name不同。)现在,nameset_size不是实例属性,也不是LotteryGameType的类属性,而是每个NewLotteryGameType的类属性。所以:

>>> SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
>>> SuperDuperLotto = make_lottery_set_type('SuperDuperLotto', 8, (1,100))
>>> SuperLotto.set_size
6
>>> SuperDuperLotto.set_size
8

如果创建这些类型的实例呢?嗯,Python在实例中查找属性,然后在最派生的类中查找属性,然后在基类中查找属性。因此,只要您不使用相同的名称创建实例属性(请注意,我从LotteryGameType.__init__方法中删除了额外的参数和设置实例属性的代码),它就会执行您想要的操作:

>>> super1 = SuperLotto([1,2,3,4,5,6])
>>> super1.set_size
6
>>> duper1 = SuperDuperLotto([1,2,3,4,5,6,7,8])
>>> duper1.set_size
8

当然,这意味着LotteryGameType本身不再是可用的类型;只有它的子类才是可用的。但这可能正是你想要的,对吧?您甚至可以考虑显式地将其设置为abstract base class,以确保没有人意外地尝试使用直接LotteryGameType实例。你知道吗

如果你觉得很勇敢,你可能想读一下元类,看看如何将整个设计改编成一个LotteryGameMetaclass,因此每个新类都是元类的一个实例,而不是(抽象)基类的一个子类。3.4中新的^{}模块的源代码,或者近似等效的外部^{}包,可能是很好的示例代码。然后你可以把两者都玩一玩,看看它们有多相似,有多不同。你知道吗

相关问题 更多 >