我需要帮助创建一个类的某些参数的对象(一系列数字)。假设我输入了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
我希望能够创建一系列数字并将其存储以供日后使用,因此我可以将其用于重载运算符(例如 eq 和 ne )
我希望能够输入Python IDLE shell:
SuperLotto([3, 4, 19, 23, 46, 27])
这将在SuperLotto的参数下创建一个对象,如果不是在'SuperLotto'的参数下(例如超过6个数字),则会引发错误。任何方法都没问题。有没有人对如何处理这个有任何想法?
答案 0 :(得分: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
即可。您可能还希望从父类或其他各种内容复制docstring。
更一般地说,看看functools.update_wrapper
(设计用于包装函数,而不是类,但许多细节是相同的)以获取灵感,并从Python版本中查看inspect
模块文档对于类可以拥有的所有属性。
在评论中,您会问:
唯一的问题是我希望NewLotteryGameType继承LotteryGameType中的name,set_size,min_set_number,max_set_number等参数。所以我想说我想在Big 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
,因此它与类属性name
不同,因为范围的工作方式。)现在,{{ 1}}和name
不是实例属性,也不是set_size
的类属性 - 但它们是每个LotteryGameType
的类属性。所以:
NewLotteryGameType
如果您创建这些类型的实例,该怎么办?好吧,Python在实例中查找属性,然后在最派生的类中查找属性,然后查找基类。因此,只要您不创建具有相同名称的实例属性(请注意我删除了额外的参数,以及设置实例属性的代码,来自>>> 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
方法),它就是您想要的:
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
不再是一个可用的类型;只有它的子类可用。但那可能就是你想要的,对吧?您甚至可以考虑将其明确设为abstract base class,以确保没有人意外尝试使用直接LotteryGameType
实例。
如果你感觉很勇敢,你可能想要阅读元类,看看如何使整个设计适应LotteryGameType
,所以每个新类都是该元类的实例而不是子类(抽象)基类。 3.4中新enum
模块的源代码或近似等效的外部flufl.enum
包可能会生成良好的示例代码。然后你可以玩两者,看看它们有多么相似和不同。