2

首先,我来自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

4 回答 4

4

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

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

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

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

对于确实需要创建 setter 和 getter 的情况,请使用property装饰器功能

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'
于 2013-05-25T15:17:55.650 回答
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
于 2013-05-25T15:17:44.460 回答
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如果您发现您确实需要该功能,您可以稍后随时制作属性(如其他一些答案中所述)。

于 2013-05-25T15:24:37.440 回答
1

如果您来自 Java/PHP,以下是您需要了解的关于 Python 如何进行面向对象的几点。


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

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

在类块中分配名称永远不会在类的实际实例上创建属性;在对象(任何对象、类或普通实例)上创建属性的唯一方法是在它们存在时分配它们。因此,类的实例所具有的所有变量的标准声明必须存在__init__于 Python 中的方法中,并在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__,并且您已经拥有对类的新实例的有效引用。

但是“对象在被调用时已经被构造__init__,并且你已经拥有对类的新实例的有效引用”准确地描述了传统构造函数的工作方式,所以我不知道作者试图在那里说明什么.

( Python 文档似乎采用的官方说法是这MyClass(arg1, arg2)是一个构造函数表达式__init__并且__new__都被描述为接收给定构造函数表达式的参数,并且__init__被明确称为构造函数。这两个术语都很少使用在文档中;他们只是说__init__大部分时间。)

因此,我敦促您忽略任何试图告诉您这__init__不是构造函数的人。__init__通过类比与构造函数来理解比试图理解它们在什么__init__是和构造函数之间做出的关键区别会做得更好。


综上所述,PHP 类的惯用翻译如下:

class MyClass(object):
    def __init__(self, myMember):
        self.myMember = myMember
于 2013-05-25T16:49:04.200 回答