2013-05-25 68 views
2

首先,我来自java和php背景,现在正在学习python。这可能因此看起来像一个愚蠢的问题,但它让我有点困惑。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

一般情况下,你不要在Python中使用getter和setter ;除非你需要改造的价值,需要的副作用期间设置或获取,或者需要防止或者获取或设置,你只需使用属性来代替:

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

实例属性是分开的类属性;在self上设置属性意味着您正在操作实例,类(MyClass.member)上的属性是类属性,因此在实例中共享。这是通过在查找属性时将类用作回退来实现的;首先查询实例,然后查询实例的类,然后查看该类的基类;设置self.member意味着任何MyClass.member在实例上不再可见。

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

对于这些情况下,您有需要创建getter和setter方法,使用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' 
+2

好吧,'__init__'是一个构造函数,就像PHP的'__construct'是一个构造函数一样。无论您是否同意这个术语,'__new__'是其他语言称为工厂的东西(有些语言支持可以方便地调用它),并且它比其他语言称为构造函数的功能严格得多(可以返回不同或已有的对象) 。所以,OP:只需使用'__init__',句号。 – delnan

+0

在我有经验的每种* OO语言中,您编写的用于初始化对象并称为构造函数的代码块将在已存在的对象上调用。因此,如果Python和PHP构造函数“不是真正的构造函数”,那么Java或C++构造函数都不是。 – Ben

+0

为什么你要将'__new__'作为静态方法?我倾向于认为它是一个类方法,因为第一个参数是一个类... – mgilson

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 
+1

双下划线最初是不合适在这里。 –

+0

谢谢我解决了! –

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然后查找上的成员,然后查找基类(按照方法解析顺序搜索)。如果它没有在任何地方找到该成员,则会产生一个AttributeError。所以,如果我实例化了我上面的类,它会打印:

__init__ called! 
3 
5 

另外请注意,我可以通过类访问类(“静态”你叫吧)成员:

print Foo.member 

或通过一个实例:

print Foo().__class__.member 

至于你会怎么写上面的PHP在Python ...答案是,你不会的。这将是最好写这样的:

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

实在没有很好的理由有二传,其唯一目的是设置一个私有成员。如果你希望用户有能力重置mymember属性,那么习惯的方式是让他们直接访问它,而不是强迫他们调用一个函数来完成这项工作。如果您发现您确实需要该功能,则可以随后将mymember置于某个属性中(如其他一些答案中所述)。

+0

'__new__'不是构造函数,'__init__'绝对是。 '__new__'在Java/C++中做了一些你不能做的事情(控制对象是如何分配的)。 Java/C++构造函数对已经存在的对象进行操作,并提供一个钩子来初始化对象的状态 - “__init__”确实是这样做的。 – Ben

+0

@Ben - 最终归结为语义。 '__new__'是负责构建新对象的方法。当你通过调用一个类来构造一个实例时,它也是第一个被调用的东西。因此,我认为它是构造函数,我想'__init__'和“初始化器”。我真的不关心在Java或C++中没有平行的东西......当然,这是我的2美分,我一次又一次看到了这个争论。最终,它只是语义学,重要的是理解语言如何工作,我希望我已经展示了...... – mgilson

+1

认真地说,我认为这种态度对新学习者的理解是有害的(只是略微,但仍然)。见证OP的困惑!术语“构造函数”已经存在了几十年,这正是“__init__”的意思。如果你不喜欢标准术语,只要将它们称为'__init__'和'__new__'(与C++/Java构造函数不同,在Python中它们总是具有相同的名称,所以我们不需要另外一个术语)。不要随意更改术语,然后根据C++构造函数不符合的标准“解释”“__init__”不是构造函数。 – Ben

1

这里有几点你需要知道Python如何做面向对象,如果你来自Java/PHP。


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

由于您通常有一个类和许多实例,类属性可以为Java类中的静态变量提供类似的用途,但它们并不完全相同。

在类块中分配名称从不在类的实际属性上创建属性; 只有在对象(任何对象,类或普通实例)上创建属性的方法是在其存在时分配它们。因此,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的属性都是“公共”的;唯一可能的担忧是你可能“有一天”想要改变getter/setter的实现,以便在不改变你的接口的情况下它们不再微不足道。 Python可以通过属性来解决这个问题(基本上允许你进行方法调用,看起来像读或写属性),所以绝对没有理由拥有getter/setter方法,除非实际上有更多的事情发生。

因此,一般来说,如果期望客户端代码操纵对象的接口包括读取和写入字段,则只需记录一个普通属性可供读取和写入。如果你想清楚一个属性(或方法)纯粹是为了内部使用,通常的惯例是用一个下划线开始名称。这会强制任何使用该名称的人知道他们正在访问内部名称(因为他们必须键入前导下划线),所以您不必担心有人会“意外”使用私有属性。


关于构造,只是对待__init__方法__construct从PHP,它会做你所期望的(但一定要记住,Python方法明确收到self参数;没有隐含this)。

有这个神话,__init__与构造函数有点不同,所以你不应该称它为一个。至于我可以告诉罪魁祸首是Dive Into Python,这相当奇怪的说:

这将是诱人的,但不正确调用这个 类的构造函数。这很诱人,因为它看起来像一个构造函数(按照约定 约定,__init__是为该类定义的第一个方法),行为 就像一个(它是在该类的新创建的 实例中执行的第一段代码),甚至听起来像一个(“初始化”当然 建议一个构造函数性质)。不正确,因为对象具有 的时间已被调用__init__被调用,并且您的 已具有对该类的新实例的有效引用。

但“对象已经由时间__init__构造被调用,并且你已经有了类的新实例的有效引用”精确表征的构造是如何传统的工作,所以我不知道什么时候作者正试图在那里。

(该出现采取官方行是MyClass(arg1, arg2)构造表达; __init____new__都描述为接收被赋予给构造表达式的所有参数,并且__init__明确地称为构造这两个术语都很少使用的文档;他们只是说__init__最重要的时间)

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


把所有在一起,你的PHP类的成语翻译将是简单如下:

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

我知道你在几年前发布过,但是我在研究Python OOP问题时偶然发现了这一点。这写得非常好。感谢您发布它。 – TMWP