V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
palmers
V2EX  ›  Python

初学 Python ,学习到 Python 继承,觉得继承/多态在 Python 中基本是废的,麻烦大家帮我解惑, 非常感谢大家!下附疑惑代码

  •  
  •   palmers · 2017-02-04 16:27:57 +08:00 · 5879 次点击
    这是一个创建于 2629 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码如下:

    class Student(object):
        def __init__(self,name,age,school):
            self.__name = name
            self.__age = age
            self.__school = school
    
        def toString(self):
            print('我是%s,我%s 岁了,在%s 上学.' %(self.__name,self.__age,self.__school))
    
    
    class XiaoMing(Student):
        def __init__(self,name,age,school):
            self.__name = name
            self.__age = age
            self.__school = school
    
        ''' 覆写父类 toString()方法'''
        # def toString(self):
        #     print('我是%s,我%s 岁了,在%s 上学.' %(self.__name,self.__age,self.__school))
    
    ''' 接受 Student 任何子类对象'''
    def fun(stu):
        stu.toString()
    
    ###测试
    xm = XiaoMing('小明',25,'北大')
    fun(xm) 
    
    

    按照继承, XiaoMing继承自 Student, 且属性是一模一样的,则toString函数再写一遍是多余的, 道理讲,应该直接使用父类的 toString方法,就可以了, 但是我去掉XiaoMingtoString方法后,就找不到 自身实例的私有属性了, 因为 python 解释器将私有属性名改变了, 我就觉得在这里多态基本都是废的, 代码复用完全无效了,

    顺便问下大家, 继承/多态 在python中还有那些有用的地方?

    第 1 条附言  ·  2017-02-04 21:13:53 +08:00

    经过各位大神的提示, 再翻阅了各个博客 官方文档 好像都是单下划线[_]标识 私有 , 然后使用

     super().__init__(name,age)
    

    实现继承,个人觉得这里好别扭啊 ,python提供了 __限制访问,但是这种方式在继承上又支持一半, 真是好不爽啊, 虽然__也不安全,但是相比 _ 感觉_就是赤身裸体站在别人面前。

    最终代码像这样:

    class Person(object):
        def __init__(self,name,age):
            self._name = name
            self._age = age
    
        def toString(self):
            print(self)
            print('hello,%s,%s' %(self._name, self._age))
    
    
    class Student(Person):
        def __init__(self,name,age):
            super().__init__(name,age)
    
    
    def fun(p):
        p.toString()
    
    
    stu = Student('学生1',23)
    fun(stu)  ## hello,学生1,23
    

    强迫症犯了 ,这点好别扭啊 !!!!! 啊啊啊啊啊啊啊啊

    第 2 条附言  ·  2017-02-04 21:23:02 +08:00

    再吐槽下python, 木有 ; 木有 { } 大爷的!!!!! 变量都不知道在哪里声明的 javasctipt 好歹还有一个 var let 大爷的~~~~~~~~

    42 条回复    2017-02-05 13:16:15 +08:00
    arischow
        1
    arischow  
       2017-02-04 16:32:49 +08:00
    你这里为什么要重写一次 XiaoMing 的 __init__?

    如果你必须重写 __init__,还可以看下这个:
    http://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p07_calling_method_on_parent_class.html
    palmers
        2
    palmers  
    OP
       2017-02-04 16:39:27 +08:00   ❤️ 1
    @arischow 咦??可以不写这个构造方法吗? 我试试 我还不知道这个啊  谢谢!!~  我看看你给的链接
    cszeus
        3
    cszeus  
       2017-02-04 16:50:05 +08:00
    class XiaoMing(Student):
    def __init__(self,name,age,school):
    Student.__init__(self, name, age, school)
    ijustdo
        4
    ijustdo  
       2017-02-04 16:56:28 +08:00
    def __str__(self):
    return '我是%s,我%s 岁了,在%s 上学.' %(self.__name,self.__age,self.__school)

    看代码 你 toString 也多余 一般都直接 __str__

    然后你 str(实例)或者 print 实例 看看呢
    palmers
        5
    palmers  
    OP
       2017-02-04 16:58:30 +08:00
    @arischow 尝试了几次不知道怎么写 你能代码展示下吗? 谢谢了
    dwood
        6
    dwood  
       2017-02-04 16:59:06 +08:00 via Android
    楼主这是用父类的来访问子类的成员变量了。其他语言也不是这么继承的吧…子类需要调用父类的构造函数。
    palmers
        7
    palmers  
    OP
       2017-02-04 17:00:36 +08:00
    @cszeus 不行 还是提示 `AttributeError: 'XiaoMing' object has no attribute '_Student__name'`
    arischow
        8
    arischow  
       2017-02-04 17:01:43 +08:00 via iPhone
    @palmers 满足你需求的话,直接给 class 写个 pass 就好了?
    chris5641
        9
    chris5641  
       2017-02-04 17:02:11 +08:00
    class XiaoMing(Student):
    def __init__(self,name,age,school):
    super().__init__(name,age,school)
    palmers
        10
    palmers  
    OP
       2017-02-04 17:02:38 +08:00
    @ijustdo 恩恩 我这个只是用来测试的  继承子类覆盖父类方法  按照道理,子类没有父类有就可以了,一样可以调用的
    palmers
        11
    palmers  
    OP
       2017-02-04 17:03:35 +08:00
    @chris5641 这种我尝试过了 不行的
    palmers
        12
    palmers  
    OP
       2017-02-04 17:06:12 +08:00
    @arischow   1#说不写构造 我就尝试了直接 pass 不行的, 我想的是, 子类如果要编写一个和父类一模一样的方法, 那子类应该就不用再重复编写了, 至于 self 应该在传递 xm 的时候就应该指向 xm 对象才对的 ,但是 根据错误提示 还是指向了 Student 对象了
    chris5641
        13
    chris5641  
       2017-02-04 17:12:35 +08:00
    LZ 你的命名有问题,如果一般的私有变量用一个下划线,两个下划线的变量通过继承是无法被覆盖的

    http://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p05_encapsulating_names_in_class.html
    dwood
        14
    dwood  
       2017-02-04 17:13:38 +08:00 via Android
    用的 python2 吧, super(Student,self).__init__(name,age,school)。
    palmers
        15
    palmers  
    OP
       2017-02-04 17:20:14 +08:00
    @chris5641 但是使用单下滑下[_]就变成 public 了.
    palmers
        16
    palmers  
    OP
       2017-02-04 17:20:51 +08:00
    @dwood 是 Python 3.6.0
    dwood
        17
    dwood  
       2017-02-04 17:26:56 +08:00
    那 def __init__(self,name,age,school):
    super().__init__(name, age, school) 这样写没问题啊。
    Allianzcortex
        18
    Allianzcortex  
       2017-02-04 19:02:34 +08:00   ❤️ 1
    @palmers pip 和 virtualenv 的作者说: Never, ever use two leading underscores.This is annoying private. 所以这个就别管 PEP8 了,@kennethreitz 写的代码都是用单下划线。 Py3.5+ 后用 super().__init__(*args,**kwargs) 来做到多继承。还有.......toString()......我猜有 Java/Scala 背景?
    phithon
        19
    phithon  
       2017-02-04 19:08:33 +08:00
    django 选手表示,多继承撑起了 class based view 的一片天……
    登录验证只需继承一个 LoginRequiredMixin ,需要表单的话再继承一下 FormMixin ,需要模板的话再继承一下 TemplateResponseMixin ,然后选择一个基本父类比如 View 、 ProcessFormView 即可完成一个 view 的编写。
    剩下的事就是写写这些父类必须的属性即可,代码量能压缩到最少。
    Allianzcortex
        20
    Allianzcortex  
       2017-02-04 19:12:19 +08:00
    @phithon 嗯, Mixin 和 CBVS 是绝配啊,避免了引入多个 utils 函数的烦恼。但我用的最多的还是 FBVS ⊙﹏⊙b 。。。。。
    guyskk
        21
    guyskk  
       2017-02-04 19:15:37 +08:00 via Android
    @palmers 单下划线也看做私有, Python 里面没有真正的私有,双下划线的属性一样有办法获取。
    edimetia3d
        22
    edimetia3d  
       2017-02-04 19:15:39 +08:00
    @chris5641 正解.

    我翻译一下:
    派生类构造时没有调用基类的构造函数,导致基类成员没有被创建,直接输出自然报错.
    palmers
        23
    palmers  
    OP
       2017-02-04 21:02:08 +08:00
    @Allianzcortex 哈哈哈哈 是啊 java 我看 python 很多特性感觉都是 javascript 和 java 的结合体 那按照你说 意思 最根本的原因是我使用了双下划线导致属性私有不能继承,所以在 toString 方法中 self 指向不到动态传递的对象 xm 上,是这样的吧?但是我在父类中 toString 方法中打印了 self 确实是子类对象呢?
    palmers
        24
    palmers  
    OP
       2017-02-04 21:03:01 +08:00
    @guyskk 这个我了解, 但是 。。 可能我有强迫症 唉 算了 我以后就单下划线标识私有吧[_]
    Allianzcortex
        25
    Allianzcortex  
       2017-02-04 21:07:40 +08:00
    @palmers 呃..什么..Python 里的 self 就等价于 Java 里的 this ,唯一不同的是 Java 已经在每个方法里把实例绑定了,不需要手动声明 this ,而 Python 的设计理念要求程序员手动把类里面方法的第一个参数显式(explicit is better than implicit 这种)声明(不用 self 而用 foo/bar/abc/def 等其他变量来声明也可以,只是 self 是约定俗成的一种表现方法)。
    palmers
        26
    palmers  
    OP
       2017-02-04 21:18:52 +08:00
    @Allianzcortex 嗯嗯 这个我也测试过 是这样的 我个人觉得这是因为 python 动态语言特性导致的。 在 python 中,动态调用方法 其实是假象,只要方法签名相同,就能找到这个方法, 即使没有继承关系,就像 javasctipt 可以将一个函数随意的绑定到别的对象上。
    PythonAnswer
        27
    PythonAnswer  
       2017-02-04 21:29:37 +08:00
    要记住, python 的世界全部是 public 。忘掉 java 就能写出来简洁的东西了。不然写出来的东西只能长成 java 的样子。
    junnplus
        28
    junnplus  
       2017-02-04 21:35:01 +08:00 via iPhone
    自己把其他语言的惯性思维带到学 python 上,怪 python 咯
    workwonder
        29
    workwonder  
       2017-02-04 21:50:21 +08:00 via Android
    对我来说, JavaScript 才是略奇怪的。
    palmers
        30
    palmers  
    OP
       2017-02-04 22:13:49 +08:00
    @PythonAnswer 嗯嗯 记住了
    palmers
        31
    palmers  
    OP
       2017-02-04 22:14:10 +08:00
    @junnplus 是的 怪 python
    palmers
        32
    palmers  
    OP
       2017-02-04 22:14:38 +08:00
    @workwonder javascript 不仅仅是奇怪呀
    mseasons
        33
    mseasons  
       2017-02-04 22:35:52 +08:00
    不用再写一遍函数,可以用父类的函数的。
    weyou
        34
    weyou  
       2017-02-04 23:10:58 +08:00 via Android
    如果你指的是传统语言的多态, python 是不支持的, python 支持的是鸭子类型,这可比多态强大多了。传统多态接受的对象必须是有相同的基类的,而 python 只要查找到对象支持某种相同的方法就可以调用。比如飞机对象有方法是“ fly()”,鸟也有相同的方法“ fly()”,你就可以写一个函数“ dofly(obj): obj.fly()”,同时接受飞机对象和鸟对象, 尽管它们不是同一个基类。
    weyou
        35
    weyou  
       2017-02-04 23:16:18 +08:00 via Android
    看了代码,感觉楼主就是想一对一翻译一下 Java 的代码,尽管 oop 思想是一致的,但要做到 pythonic 还是要系统的按部就班的学习一下 python
    greatonce
        36
    greatonce  
       2017-02-04 23:50:52 +08:00
    __init__ 不是构造方法,这个是初始方法

    __new__ 才是构造方法
    Gem
        37
    Gem  
       2017-02-05 00:28:27 +08:00
    看到 toString(),莫名其妙的想到了 Scala...
    yinian1992
        38
    yinian1992  
       2017-02-05 03:40:54 +08:00
    toString 的话可以重写 __str__ 。
    20015jjw
        39
    20015jjw  
       2017-02-05 03:44:20 +08:00 via Android
    建议你在骂语言辣鸡的之前 好好学一下
    tairan2006
        40
    tairan2006  
       2017-02-05 07:01:49 +08:00 via Android
    Python 的思路和 Java 完全不一样…另外楼主你的用法有问题
    ryd994
        41
    ryd994  
       2017-02-05 08:36:48 +08:00 via Android
    道理很简单,因为你在子类里又声明了一套同名变量,而父类里试图打印的是父类里的变量。名可名非常名
    你怎么不在 C++里继承然后声明同名变量试试? C++也辣鸡咯?
    palmers
        42
    palmers  
    OP
       2017-02-05 13:16:15 +08:00
    @greatonce 非常感谢! 我知道了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5513 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 06:58 · PVG 14:58 · LAX 23:58 · JFK 02:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.