(类变量的用法)pythonic - 或从java学习的讨厌习惯?

时间:2010-09-29 21:41:09

标签: class coding-style python

你好Pythoneers:下面的代码只是模仿我正在尝试做的事情,但它应该说明我的问题。

我想知道这是否是我从Java编程中获取的肮脏技巧,或者是有效的Pythonic做事方式:基本上我正在创建一堆实例,但我需要跟踪'静态'数据创建时的所有实例。

class Myclass:
        counter=0
        last_value=None
        def __init__(self,name):
                self.name=name
                Myclass.counter+=1
                Myclass.last_value=name

使用这个简单类的一些输出,表明一切都按预期工作:

>>> x=Myclass("hello")
>>> print x.name
hello
>>> print Myclass.last_value
hello
>>> y=Myclass("goodbye")
>>> print y.name
goodbye
>>> print x.name
hello
>>> print Myclass.last_value
goodbye

这是一种普遍接受的做这种事情的方式,还是一种反模式?

[例如,我不太高兴我显然可以在课堂上(好)和外面(好)设置柜台;也不热衷于在类代码本身中使用完整的命名空间'Myclass' - 只是看起来很笨重;最后,我最初将值设置为“无” - 这可能是我通过这样做打乱静态类型的语言?]

我使用的是Python 2.6.2,程序是单线程的。

5 个答案:

答案 0 :(得分:7)

在我看来,类变量完全是Pythonic。

请注意一件事。实例变量可以隐藏类变量:

x.counter = 5  # creates an instance variable in the object x.
print x.counter  # instance variable, prints 5
print y.counter  # class variable, prints 2
print myclass.counter # class variable, prints 2

答案 1 :(得分:3)

执行。不。有。有状态。类。变量

这是调试的噩梦,因为类对象现在具有特殊功能。

有状态类会混淆两(2)个不相关的职责:对象创建状态和创建的对象。不要把责任分开,因为它“似乎”就像它们属于一体。在此示例中,创建对象的计数是Factory的责任。创建的对象具有完全不相关的职责(不容易从问题中推断出来)。

另外,请使用大写字母类名称。

class MyClass( object ):
    def __init__(self, name):
            self.name=name

def myClassFactory( iterable ):
   for i, name in enumerate( iterable ):
       yield MyClass( name )

序列计数器现在是工厂的一部分,应保持状态和计数。在一个单独的工厂。

[对于玩Code Golf的人来说,这更短。但那不是重点。重点是该课程不再是有状态的。]

从问题中不清楚如何创建Myclass实例。缺乏任何线索,关于如何使用工厂没有太多可说的。可迭代是通常的罪魁祸首。也许是通过列表或文件或其他可迭代数据结构进行迭代的东西。

此外 - 对于刚刚从Java船上来的人 - 工厂对象只是一个功能。不需要更多。


由于关于这个问题的例子非常不清楚,因此很难知道为什么(1)用(2)计数器创建两个唯一对象。两个唯一对象已经是两个唯一对象,不需要计数器。

例如,Myclass中的静态变量从不在任何地方引用。这使得理解这个例子非常非常困难。

x, y = myClassFactory( [ "hello", "goodbye" ] ) 

如果实际用于某事物的计数或最后一个值,则可以创建一个可能有意义的例子。

答案 2 :(得分:2)

您不必在此处使用类变量;这是使用全局变量的完全有效的案例:

_counter = 0
_last_value = None
class Myclass(obj):
    def __init__(self, name):
        self.name = name

        global _counter, _last_value
        _counter += 1
        _last_value = name

我有一种感觉,有些人会因为习惯而反对全局性,所以快速回顾可能是因为全局错误 - 而不是错误 - 。

Globals传统上是程序中任何位置可见且可更改的变量 unscoped 。这是像C这样的语言中的全局变量的问题。它与Python完全无关;这些“全局”是作用于模块的。班级名称“Myclass”同样是全球性的;这两个名称的范围相同,在它们所包含的模块中。大多数变量 - 在Python中与C ++相同 - 在逻辑上是对象实例的一部分或本地范围,但这是在类的所有用户中清除共享状态。

我没有强烈反对使用类变量(并且使用工厂是完全没必要的),但是全局变量是我通常会这样做的。

答案 3 :(得分:2)

您可以通过将代码拆分为两个单独的类来解决此问题。

第一个类将用于您要创建的对象:

class MyClass(object):
    def __init__(self, name):
        self.Name = name

第二个类将创建对象并跟踪它们:

class MyClassFactory(object):
    Counter = 0
    LastValue = None

    @classmethod
    def Build(cls, name):
        inst = MyClass(name)
        cls.Counter += 1
        cls.LastValue = inst.Name
        return inst   

这样,您可以根据需要创建类的新实例,但有关创建的类的信息仍然是正确的。

>>> x = MyClassFactory.Build("Hello")
>>> MyClassFactory.Counter
1
>>> MyClassFactory.LastValue
'Hello'
>>> y = MyClassFactory.Build("Goodbye")
>>> MyClassFactory.Counter
2
>>> MyClassFactory.LastValue
'Goodbye'
>>> x.Name
'Hello'
>>> y.Name
'Goodbye'

最后,这种方法避免了实例变量隐藏类变量的问题,因为MyClass实例不知道创建它们的工厂。

>>> x.Counter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute 'Counter'

答案 4 :(得分:1)

这是pythonic吗?嗯,它肯定比计数器的全局变量和最新实例的值更具pythonic。

在Python中说,只有一种正确的方法可以做任何事情。我想不出更好的方法来实现这一点,所以继续前进。尽管许多人会批评你对问题的“非pythonic”解决方案(比如Java编写的不必要的面向对象或者许多来自C和C ++的“自己动手”的态度),在大多数情况下你的Java习惯不会把你送到Python地狱。

除此之外,谁在乎它是否是“pythonic”?它有效,而且不是性能问题,是吗?