0

关于类的某些参数,我需要有关创建对象(数字序列)的帮助。假设我输入了 Python IDLE 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

它将使“SuperLotto”成为名为“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 IDLE shell 中输入:

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

这将在 SuperLotto 的参数下创建一个对象,如果不在“SuperLotto”的参数下(比如超过 6 个数字),则会引发错误。任何方法都可以。有没有人对如何解决这个问题有任何想法?

4

1 回答 1

0

听起来您想要的是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

(显然__eq__,如果 set-equality 不是您使用的正确规则,您可以随意定义。)


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

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

为此,只需添加NewLotteryGameType.__name__ = name. 您可能还想从父类或其他各种东西中复制文档字符串。

更一般地,请查看functools.update_wrapper(旨在包装函数,而不是类,但许多细节是相同的)以获取灵感,以及inspect来自 Python 版本的模块文档,了解类可以具有的所有属性。


在评论中,你问:

唯一的问题是我想让NewLotteryGameType从LotteryGameType继承name、set_size、min_set_number、max_set_number等参数。所以假设我想在 Python Shell 中输入 NewLotteryGameType.set_size。我希望它还给我 6。

这是矛盾的。如果你想继承LotteryGameType……的实例属性,你已经这样做了。例如:

>>> super1.set_size
6

如果您希望它们可以从类中访问,那么它们不能是实例属性,它们必须是类属性。并且仅仅更改set_size为类属性LotteryGameType并继承它是行不通的,因为类属性的全部意义在于该类或其任何子类的所有实例共享相同的值,并且子类都需要不同的值。

但你可以这样做:

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因此它与类属性不同。)现在,它们不是实例属性,也不是类属性——但它们是类属性每个. 所以:namenameset_sizeLotteryGameTypeNewLotteryGameType

>>> 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本身不再是可用的类型;只有它的子类可用。但这可能就是你想要的,对吧?您甚至可以考虑将其显式设为抽象基类,以确保没有人不小心尝试使用直接LotteryGameType实例。

如果您有勇气,您可能想阅读元类并了解如何将整个设计调整为使用 a LotteryGameMetaclass,因此每个新类都是该元类的实例,而不是(抽象)基类的子类。3.4中新enum模块的源代码,或几乎等效的外部flufl.enum包,可能是很好的示例代码。然后,您可以同时玩两者,看看它们有多么相似和有多么不同。

于 2013-08-10T00:30:07.513 回答