你好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,程序是单线程的。
答案 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”?它有效,而且不是性能问题,是吗?