Python类成员混淆

时间:2013-05-25 15:13:30

标签: php python oop

首先,我来自java和php背景,现在正在学习python。因此,这似乎是一个愚蠢的问题,但它让我有点困惑。

作为php中的一个例子:

class MyClass
{
   private $myMember;

   public function __construct($myMember = null)
   {
       $this->myMember = $myMember;
   }

   public function setMyMember($myMember)
   {
       $this->myMember = $myMember;
   }

   public function getMyMember()
   {
       return $this->myMember;
   }

}

这完全是教科书101 OOP。具有单个私有成员以及getter和setter的类。

然而,学习一些python并不是这么简单。

首先,构造函数不是一个真正的构造函数(但你可以使用吗?)

其次,任何级别的减速都被视为静态?所以python中的self.myMember实际上在实例上设置了一个与myMember类不同的变量?

最后,如果我的混淆是正确的 - 上面的相同例子将如何在python中编码?

4 个答案:

答案 0 :(得分:4)

通常,您不在Python中使用getter和setter;除非您需要转换值,在设置或获取期间需要副作用,或者需要阻止获取或设置,否则您只需使用属性:

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

实例属性与类属性分开;在self上设置属性意味着您正在操作实例,类(MyClass.member)上的属性是类属性,因此在实例之间共享。这是通过在查找属性时使用类作为后备来实现的;首先查询实例,然后是实例的类,然后是类的基类;设置self.member表示实例上不再显示任何MyClass.member

从技术上讲,__init__方法确实不是构造函数。调用self时已存在__init__。它是初始化器,您可以使用它设置第一个值。如果您需要实际的构造函数(实例工厂),则需要查找__new__ static method。请注意,在PHP中,__construct也是初始值设定项,而不是构造函数!

对于需要创建setter和getter的情况,请使用property decorator function

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

    @property
    def member(self):
        return 'Hello {}!'.format(self._member)

    @member.setter
    def member(self, value):
        # Remove "Hello " and "!" from the start and end of any value being set.
        if value.startswith('Hello '):
            value = value.split(None, 1)[1]
        self._member = value.rstrip('!')

演示:

>>> m = MyClass('World')
>>> m.member
'Hello World!'
>>> m.member = 'Hello Planet Earth!'
>>> m.member
'Hello Planet Earth!'
>>> m._member
'Planet Earth'

答案 1 :(得分:3)

这将是这样的:

class MyClass(object):

    def __init__(self, myMember=None):
        self._myMember = myMember

    @property
    def myMember(self):
        return self._myMember

    @myMember.setter
    def myMember(self, value):
        self._myMember = value

演示:

c = MyClass()
c.myMember = 10
print c.myMember

你有:

# 10

答案 2 :(得分:1)

在python中,类创建一个新的命名空间。除了有趣的业务(如元类),命名空间中的东西成为的属性。

class Foo(object): #inherit from object in python2.x ... It's a good idea
     class_variable = 3
     __not_really_private_but_sort_of = 7

现在我创建了一个有2个属性的类。第一个是“公开”。第二个是“私有”,因为它带有双下划线作为前缀,调用python的名称。在python中,这是很少使用。毕竟,我们都在这里同意程序员:)。

当你调用时,它调用__new__方法(构造函数)然后(如果__new__返回该类的实例)它调用初始化程序__init__)。大多数情况下,您不需要对__new__执行任何操作 - 默认__new__做得很好。由于__init__是常规方法,因此它接收类的实例作为第一个参数 - 基本上填充来自其他语言的this->指针的角色:

class Foo(object):
    member = 4
    other_member = 5
    def __init__(self):
        print "__init__ called!"
        self.member = 3
        print self.member
        print self.other_member

与其他语言相比,python有点奇怪的事实是,如果一个实例没有成员,那么python会在上查找成员然后在基础上查找类(在方法解析顺序中搜索)。如果它在任何地方找不到该成员,那么它会引发AttributeError。所以,如果我实例化我的上面的类,它将打印:

__init__ called!
3
5

另请注意,我可以通过类访问类(您调用它的“静态”)成员:

print Foo.member

或通过实例:

print Foo().__class__.member

至于你如何在python中编写上面的PHP ...答案是你不会。最好这样写:

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

确实没有充分理由让一个setter设置私人成员。如果您希望用户能够重置mymember属性,那么惯用的方法是让他们直接访问它,而不是强迫他们调用函数来完成工作。如果您发现实际上需要该功能,您可以随后将mymember变为属性(如其他一些答案中所述)。

答案 3 :(得分:1)

如果您来自Java / PHP,那么您需要了解有关Python如何面向对象的几点。


在Python中,类也是完全普通的对象。这意味着类本身可以有成员变量(我们通常只称它们为属性)。您在类块中分配的所有名称都将成为类本身的属性,而不是其任何实例的属性(包括创建方法的函数def块;所有方法都在Python中)。< / p>

由于您通常只有一个类和多个实例,因此类属性可以用于与Java类中的静态变量类似的目的,但它们的概念并不完全相同。

在类块中分配名称​​从不在类的实际内容中创建属性; only 在对象(任何对象,类或普通实例)上创建属性的方法是在它们存在时分配它们。所以所有变量的标准声明都是一个类&#39;实例必须使用Python中的__init__方法,在self上分配属性。

例如:

class Foo(object):
    foo = "I am a class attribute"

    def __init__(self):
        self.bar = "I am an instance attribute"

这常常使新的Python程序员感到困惑的是,尝试从实例中读取 foo属性实际上会起作用,并找到类属性(这是方法调用找到函数的方式)他们应该打电话)。所以它看起来像是在类块中声明实例属性,以便第一次休闲玩耍。但实际上只有一个属性foo,在所有实例之间共享,如果它是一个可变对象并且您希望每个实例都有自己的副本,则可能非常糟糕。


Python中没有访问控制,所以没有私有变量。这是因为Python足够动态,你可以随时解决任何这样的限制,使它们毫无意义。

相反,Python程序员倾向于依赖文档和约定来确保编写客户端代码的程序员知道哪些名称是公共API的一部分,哪些是实现细节。然后我们相信每个人都会尊重这些规则,那些不必处理他们的代码的人就不应该依赖于变更。

在任何语言中,具有琐碎的getter和setter的属性实际上都是&#34; public&#34;无论如何;唯一可能的问题是你可能会在某一天&#34;想要改变getter / setter的实现,以便在不改变你的界面的情况下它们不再是微不足道的。 Python可以通过属性来解决这个问题(基本上允许你进行看起来像读取或写入属性的方法调用),所以绝对没有理由使用getter / setter方法,除非实际上有更多的东西继续他们。

通常,如果您希望客户端代码操作对象的接口包括读取和写入字段,则只需记录可读取和写入的普通属性。如果您想清楚属性(或方法)纯粹是供内部使用,通常的惯例是使用单个下划线启动名称。这迫使使用该名称的任何人知道他们正在访问内部名称(因为他们必须键入前导下划线),因此您不必担心有人会意外地&#34;使用私有属性。


关于构造函数,只需将__init__方法视为来自PHP的__construct,它就会按预期执行(但请确保您记得Python方法明确接收self参数;无隐式this)。

这个神话围绕着__init__与构造函数有些不同,所以你不应该把它称为一个。据我所知,罪魁祸首是Dive Into Python,其中有些奇怪的说:

  

将它称为构造函数是很诱人但不正确的   类。这很诱人,因为它看起来像一个构造函数(由   约定,__init__是为类定义的第一个方法),act   像一个(它是新创建的第一段代码)   类的实例),甚至听起来像一个(“init”肯定   建议一个构造函数 - 自然)。不正确,因为对象有   已被__init__召唤的时候已经建成了   已经有了对该类新实例的有效引用。

但是&#34;对象已经在__init__被调用的时候被构造,并且你已经有了对该类的新实例的有效引用&#34;精确地描述了传统建构者的工作方式,所以我不知道作者试图在那里做什么。

Python docs似乎采用的官方行是MyClass(arg1, arg2)构造函数表达式; __init____new__都被描述作为接收给予构造函数表达式的参数,__init__被明确地称为构造函数。这两个术语在文档中很少使用;他们只是在所有时间都说__init__ 。)

所以我敦促你忽略试图告诉你__init__不是构造函数的人。通过类比构造函数来尝试理解__init__,而不是试图理解他们在__init__和构造函数之间做出的关键区别,你会做得更好。


总而言之,PHP类的惯用翻译将简单如下:

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