【问题标题】:Need help on creating object with class在使用类创建对象时需要帮助
【发布时间】:2013-08-09 23:48:00
【问题描述】:

关于类的某些参数,我需要有关创建对象(数字序列)的帮助。假设我输入了 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 个数字),则会引发错误。任何方法都可以。有人对如何解决这个问题有任何想法吗?

【问题讨论】:

  • 请使用python标签表明您的问题是关于Python的。
  • 谢谢提醒。
  • SuperLotto 不是一个类,它是一个全局变量,一个LotteryGameType 类型的实例,您通过调用make_lottery_set_type 创建。因此,它不会像函数一样可调用,除非您将 __call__ 方法添加到 LotteryGameType。这可能不是你想要的。退后一步,解释你为什么要调用它。也许你真的希望它是一个类或一个函数,而不是一个实例?
  • 让我们看看.. 我想你明白了我试图弄清楚的想法。方法是 SuperLotto 是一个类,所以我可以在 shell 中输入: super1 = SuperLotto([1,2,3,4,5,6]) super2 = SuperLotto([2,3,4,5,6, 7]) 我可以比较这两个:super1 == super2,它会返回 False。
  • 所以您想编写一个返回类的make_lottery_set_type,而不是LotteryGameType 实例?好的,这是可行的。

标签: python class python-3.x parameters constructor


【解决方案1】:

听起来您想要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。您可能还想从父类或其他各种东西中复制文档字符串。

更一般地,查看functools.update_wrapper(它是为封装函数而不是类,但许多细节是相同的)以获得灵感,以及来自您的 Python 版本的 inspect 模块文档类可以具有的属性。


在评论中,你问:

唯一的问题是我想让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 不同。)现在,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 中新的enum 模块的源代码,或几乎等效的外部flufl.enum 包,可能是很好的示例代码。然后,您可以同时使用它们,看看它们的相似之处和不同之处。

【讨论】:

  • 哇,非常感谢!我们可以在 NewLotteryGameType 下添加重载运算符对吗?编辑:我刚刚在它下面添加了 repr 。它有效。这也适用于 neeq 吗? :)
  • @user2559679:我已经在它下面添加了__eq__。所以当然你也可以添加__ne__
  • 嗯,我的程序差不多完成了。唯一的问题是我想让NewLotteryGameTypeLotteryGameType继承name, set_size, min_set_number, max_set_number等参数。所以假设我想在 Python Shell 中输入NewLotteryGameType.set_size。我希望它返回给我6。关于如何做到这一点的任何想法?
  • 你的要求是矛盾的。在LotteryGameType 中,set_size 是实例属性,而不是类属性。它会完全按照您的意愿继承:super1.set_size 将返回 6。但这意味着您不能从类中访问它,只能从类的实例中访问。如果你想让它成为一个类属性,你可以……但是它不是从LotteryGameType 继承的,它是由每个新的子类定义的。那么……你想要哪一个?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-16
  • 1970-01-01
  • 1970-01-01
  • 2020-03-06
相关资源
最近更新 更多