python:实例化类与引用的任何缺点,如果它只有常量?

时间:2013-02-21 00:59:47

标签: python

我对python很新,所以如果这个问题听起来很愚蠢我会道歉。这不仅仅是普通的好奇心,我必须使用具有类似类的代码。

考虑以下python代码片段:

class _Base(object):
  constant1 = 1
  constant2 = 2
  constant3 = 3


def main():
  a = _Base    # Referencing a class
  b = _Base()  # Instantiating


if __name__ == '__main__':
  main()

在这个特定示例中,当_Base类没有__init__()方法时,与b相比,使用a方法是否有任何缺点,性能方面或其他方面?< / p>

4 个答案:

答案 0 :(得分:4)

通常将常量放在模块中而不是类中。如果你需要将它们用于子类,那么继承Base并使用它们,否则,不要实例化class,因为你只是将它用作一种“命名空间”。

将其命名为优于_Base并将变量视为(例如:) MyConstants.constant1而不是......

答案 1 :(得分:3)

Jon Clements给出了如何做到这一点的答案。

但要回答你的实际问题:

  与p

相比,使用b方法是否有任何缺点,性能方面或其他方面

比性能更重要的是可读性。如果你实例化一个对象,读者会认为你已经出于某种原因这样做了,并且试图找出正在使用b的内容以及_Base实例在你的对象中代表的内容模型等。要弄清楚它是没用的,不会花太长时间,但“显而易见”总是好于“不会花太长时间才弄明白”。

但也存在性能下降。在您编写的任何程序中,它很可能永远不会以任何可测量的方式发生,但它就在那里。

首先,b是一个新分配的对象,需要几个字节(可能是几十个),而a只是已存在对象的新名称(类本身) 。所以,它浪费了记忆。

其次,构建b需要一些时间。除了分配内存外,您还必须拨打__new__上的__init__object位置。

您可以使用timeit测试自己的性能差异,但我不会打扰。你很可能会发现ba或类似的东西慢20倍,但是你每次运行一次,在一微秒内完成一次20:1的改进仍然没有意义

答案 2 :(得分:0)

@Jon Clements的答案非常好,但如果你愿意,你可以继续使用该类,但将所有常量转换为静态方法。

class MyConstants(object):

    @staticmethod
    def constant1():
        return 1

然后你可以打电话给它:

some_variable = MyConstants.constant1()

我认为处理这样​​的事情在可维护性方面更好 - 如果你想做除了返回常量以外的任何事情,Jon的解决方案将不起作用,你将不得不重构你的代码。例如,您可能希望在某个时刻更改constant1的定义:

def constant1():
    import time
    import math

    current_time = time.time()        
    return math.ceil(current_time)

将当前时间返回到最接近的秒。

无论如何,对于这篇文章感到抱歉:)


所以,鉴于这里的评论,我认为我会看到实际开销是什么(使用工厂)而不是声明静态常量与使用类中的属性。

time_test.py

import time

CONSTANT_1 = 1000
CONSTANT_2 = 54
CONSTANT_3 = 42
CONSTANT_4 = 3.14

class Constants(object):
    constant_1 = 1000
    constant_2 = 54
    constant_3 = 42
    constant_4 = 3.14

class Factory(object):

    @staticmethod
    def constant_1():
        return 1000

    @staticmethod
    def constant_2():
        return 54

    @staticmethod
    def constant_3():
        return 42

    @staticmethod
    def constant_4():
        return 3.14

if __name__ == '__main__':

    loops = 10000000

    # static const                                                                                                                                                                                                                                                              
    start = time.time()
    for i in range(loops):
        sum = CONSTANT_1
        sum += CONSTANT_2
        sum += CONSTANT_3
        sum += CONSTANT_4

    static_const_time = time.time() - start

    # as attributes                                                                                                                                                                                                                                                             
    start = time.time()
    for i in range(loops):
        sum = Constants.constant_1
        sum += Constants.constant_2
        sum += Constants.constant_3
        sum += Constants.constant_4

    attributes_time = time.time() - start

    # Factory                                                                                                                                                                                                                                                                   
    start = time.time()
    for i in range(loops):
        sum = Factory.constant_1()
        sum += Factory.constant_2()
        sum += Factory.constant_3()
        sum += Factory.constant_4()

    factory_time = time.time() - start

    print static_const_time / loops
    print attributes_time / loops
    print factory_time / loops

    import pdb
    pdb.set_trace()

结果:

Bens-MacBook-Pro:~ ben$ python time_test.py
4.64897489548e-07
7.57454514503e-07
1.09821901321e-06
--Return--
> /Users/ben/time_test.py(71)<module>()->None
-> pdb.set_trace()
(Pdb) 

所以你有它:效率的边际收益(每千万个循环几秒钟),可能会被代码中其他地方的东西所淹没。所以我们已经确定所有三种解决方案都具有相似的性能,除非你关心这样的微优化。 (如果是这种情况,你可能最好在C中工作。)所有这三种解决方案都是可读的,可维护的,并且可能在任何使用Python的软件公司的版本控制中都可以找到。所以区别在于美学。

无论如何,由于我的参考书目格式不正确,我曾在高中的研究论文中丢失了15%的积分。内容完美无瑕,对我的老师来说还不够。我发现人们可以花时间学习规则或解决问题。我更喜欢解决问题。

答案 3 :(得分:0)

我说要注意将来的维护,并将类设置为具有所有@property方法,因为它们与典型常量无法区分,并且允许代码增长,因为所有内容都是{的实例{1}},并包含在方便的_Base装饰器中。

property

当然,我的下一个问题是......为什么你需要实例化才能做到这一点?为什么不在方法中定义常量?

class Base(object):
    """for now, just holds constants"""
    def __init__(self):
        pass

    @property
    def constant1(self):
        return 1

    @property
    def constant2(self):
        return 2

    @property
    def constant3(self):
        return 3

您可以在方法和类中引用它们,而不将它们作为参数提供。它们作为普遍的数据源工作,就像常数应该一样。这通常是避免magic numbers的方法。