听起来您想要的是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
因此它与类属性不同。)现在,它们不是实例属性,也不是类属性——但它们是类属性每个. 所以:name
name
set_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
本身不再是可用的类型;只有它的子类可用。但这可能就是你想要的,对吧?您甚至可以考虑将其显式设为抽象基类,以确保没有人不小心尝试使用直接LotteryGameType
实例。
如果您有勇气,您可能想阅读元类并了解如何将整个设计调整为使用 a LotteryGameMetaclass
,因此每个新类都是该元类的实例,而不是(抽象)基类的子类。3.4中新enum
模块的源代码,或几乎等效的外部flufl.enum
包,可能是很好的示例代码。然后,您可以同时玩两者,看看它们有多么相似和有多么不同。