繁体   English   中英

Python类语法 - 这是一个好主意吗?

[英]Python class syntax - is this a good idea?

我很想定义我的Python类:

class MyClass(object):
    """my docstring"""

    msg = None
    a_variable = None
    some_dict = {}

    def __init__(self, msg):
        self.msg = msg

声明对象变量(msg,a_variable等)在顶部,如Java好还是坏或无关紧要? 我知道这是不必要的,但仍然很有诱惑力。

在类定义中定义变量就像这样,使得变量可以在该类的每个实例之间访问。 在Java术语中,它有点像使变量静态。 但是,如下所示,存在重大差异。

class MyClass(object):
    msg = "ABC"

print MyClass.msg     #prints ABC
a = MyClass()
print a.msg           #prints ABC
a.msg = "abc"
print a.msg           #prints abc
print MyClass.msg     #prints ABC
print a.__class__.msg #prints ABC

从上面的代码可以看出,它并不完全相同,因为变量可以通过self.msg进行访问,当它被赋值时,它不会被分配给类范围中定义的变量。

通过你所做的方法做到这一点的一个缺点是,它会导致错误,因为它会增加类的隐藏状态。 假设有人从构造函数中遗漏了self.msg = "ABC" (或者更实际的代码被重构,只有一个定义被改变)

a = MyClass()
print a.msg   #prints ABC

#somewhere else in the program
MyClass.msg = "XYZ"

#now the same bit of code leads to a different result, despite the expectation that it
#leads to the same result.
a = MyClass()
print a.msg   #prints XYZ

最好避免在类级别定义msg然后避免问题:

class MyClass(object):
    pass

print MyClass.msg #AttributeError: type object 'MyClass' has no attribute 'msg'

直接在类定义中声明变量使它们成为类变量而不是实例变量。 类变量有点类似于Java中的静态变量,应该像MyClass.a_variable一样使用。 但它们也可以像self.a_variable一样self.a_variable ,这是一个问题,因为天真的程序员可以将它们视为实例变量。 例如,你的“some_dict”变量将由MyClass的每个实例共享,因此如果你向它添加一个键“k”,那么任何实例都可以看到它。

如果你总是记得重新分配类变量,那么实例变量几乎没有区别。 只保留MyClass的初始定义。 但无论如何,这不是一个好习惯,因为在不重新分配这些变量时可能会遇到麻烦!

最好像这样编写类:

class MyClass(object):
    """
    Some class
    """

    def __init__(self, msg):
        self.__msg = msg
        self.__a_variable = None
        self.__some_dict = {}

使用两个下划线表示“私有”变量( 伪私有! )是可选的。 如果变量应该是公共的,只需保留其名称不带__前缀。

小心。 这两个msg属性实际上存储在两个不同的字典中。 一个遮蔽另一个,但破坏的msg属性仍然占用字典中的空间。 所以它没有使用,但仍占用一些内存。

class MyClass(object):    
    msg = 'FeeFiFoFum'   
    def __init__(self, msg):
        self.msg = msg

m=MyClass('Hi Lucy')

请注意,我们将'Hi Lucy'作为值。

print(m.__dict__)
# {'msg': 'Hi Lucy'}

请注意,MyClass的dict(通过m.__class__访问)仍然具有FeeFiFoFum

print(m.__class__.__dict__)
# {'__dict__': <attribute '__dict__' of 'MyClass' objects>, '__module__': '__main__', '__init__': <function __init__ at 0xb76ea1ec>, 'msg': 'FeeFiFoFum', 'some_dict': {}, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': 'my docstring', 'a_variable': None}

另一种(也许更简单)的方式来看到这个:

print(m.msg)
# Hi Lucy
print(MyClass.msg)
# FeeFiFoFum

当你声明一个类时,Python将解析它的代码并将所有内容放在类的命名空间中; 那么这个类将被用作从它派生的所有对象的一种模板 - 但是任何对象都有自己的引用副本。
请注意,您始终有参考; 因此,如果您能够更改引用的对象,则更改将反映到它正在使用的所有位置。 但是,成员数据的插槽对于每个实例都是唯一的,因此将其分配给新对象将不会反映到正在使用的任何其他位置。

注意:Michael Foord有关于类实例化如何工作的非常好的博客文章 ; 如果你对这个话题感兴趣,我建议你阅读一下。

无论如何,对于所有实际用途,两种方法之间存在两个主要差异:

  1. 该名称已在类级别可用,您可以在不实例化新对象的情况下使用它; 这可能听起来很好用于在命名空间中声明常量,但在许多情况下,模块名称可能已经很好了。
  2. 该名称是在类级别添加的 - 这意味着您可能无法在单元测试期间轻松地模拟它,并且如果您有任何昂贵的操作,您可以在导入的那一刻获得它。

通常,审查代码我看到在班级宣布的成员有点怀疑; 它们有很多很好的用例,但它们很可能是以前使用其他编程语言的习惯。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM