zoukankan      html  css  js  c++  java
  • Python : Class

      Python 和 JavaScript一样即是面向过程语言,也是面向对象语言,动态语言。大多数面向对象语言里,Class是必不可少的。面向对象有三大特性:封装, 继承,多态。在Python中Class到底是怎样的呢?

    1、Class组成

      先来看一个示例:

    class Person(object):
        id=''
        name = ''
        age = 3
        
        # 等同于Java中的<init>,即构造器
        def __init__(self, id, name, age):
            print("init a Person instance")
            self.id = id
            self.name = name
            self.age = age
    
        def show(this):
            print(this)
        #   print(this.toString())
    
        #def toString(self):        
        #    return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age)
        
        # 等同于Java中的toString
        def __str__(self):
        #   return self.toString()
            return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age)
        
    
        # 等同于Java中的finalize方法,del 实例时调用
        def __del__(self):
            print("finally a Person instance")
            self.id = None
            self.name = None
            self.age = None
            self = None
        


      下面是以对比Java的方式,来说明Python中的类: 

      1)Class中,包括属性、方法,它们都是public的。在Python的Class中,是不存在private,protected等修饰符的。

      2)__init__是构造函数,调用构造器时,会自动调用__init__。它相当于Java中的<init>。在创建一个Python对象时,不需要像Java那样使用new。

      3)__del__是析构函数,当del instance时,会自动调用__del__。它相当于Java中的finalize

      4)需要获取对象的字符串表示时,会调用__str__。它就相当于Java中的toString。

      5)类的方法的第一个参数,都是self,相当于Java中的this。其实Java中的实例方法的第一个参数也是this,只是被隐藏了而已,如果你了解JVM 运行时的话,或者使用过 javassist或者asm等字节码工具的话,会知道的。上面的示例中的p1.show()运行时,可以理解为执行的是Person.show(p1)。 并不是说必须得写成self,你也可以写完this或者其他任何的满足变量命名的形式。但通常大家都约定俗称的写为self,保持编码风格的统一,有利于方便他人理解代码。

      6)__init__,__del__,__str__不是必须的。

      7)所有属性都要有初始值

    2、Class getter,setter

      Java,JavaScript ES6都支持setter,getter。Python中也是支持的。

    class Person(object):
        id=''
        name = ''
        age = 3
        
        # 等同于Java中的<init>,即构造器
        def __init__(self, id, name, age):
            print("init a Person instance")
            self.id = id
            self.name = name
            self.age = age
    
        def show(this):
            print(this)
        #    print(this.toString())
    
       #def toString(self):        
       #   return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age)
        
        # 等同于Java中的toString
        def __str__(self):
       #   return self.toString()
            return "id:{}, name:{}, age:{}".format(self.id, self.name, self.age)
        
    
        # 等同于Java中的finalize方法,del 实例时调用
        def __del__(self):
            print("finally a Person instance")
            self.id = None
            self.name = None
            self.age = None
            self = None
    
        def __get__(self, name):
            print("invoke in __get__")
            print(self.__dict__)
            return 1111
    
        def __getattr__(self, name):
            print("invoke in __getattr__")
            return 1111
    
        def __getattribute__(self, name):
            print("invoke in __getattribute__")
            print(object.__getattribute__(self, name))
            print("after invoke in __getattribute__")
            return object.__getattribute__(self, name)

      对于旧式类,访问属性的顺序是: 

      1)直接访问属性

      2)访问__getattr__

      对于新式类,访问属性的顺序是:

      1)__getattribute

      2)直接访问属性

      3)__getattr__

      切记,如果写了__getattribute__,最后一句话必须是object.__getattribute(self,name) 否则就会出现:’XxxType’ object is not callable 。它的存在,更像是作为拦截器使用。

    3、Class继承

      C++是多继承的,Java是单继承的,Python借鉴了C++的多继承方式。

      在继承结构下,访问属性,方法时,与Java中一样的,先自己的,自己没有时,才去从父类找。

      由于支持多继承,所以当一个属性、方法,在多个父类中都存在时,会访问到哪个呢?

      要解答这个问题,就得先知道搜索属性、方法的顺序。采用的搜索算法是深度优先搜索算法。

      也就是一个class A(S1,S2,S3):pass; 要调用一个属性时,会先从A里找,找不到再从S1,如果还找不到再从S2,依次类推。

      那么在多继承情况下,如果A,S1,S2,S3都重写了__getattr__方法,那会有什么区别呢?查找顺序大体不变的:

      1)  A的直接属性,找不到然后是A.__getattr__

      2)  S1的直接属性,找不到然后是S1.__getattr__

      3)  S2的直接属性,找不到然后是S2.__getattr__

      4)  S3的直接属性,找不到然后是S3.__getattr__

      对于构造函数__init__,在构造实例时,不会像Java那样,先去调用父类的__init__。

    class Student(Person):
        def __init__(self, id, name, age,email):
            print("invoke in Student __init__")
            super(Student, self).__init__(id, name, age)
            self.email = email

      Java中有super(),Python中是否有呢?能否利用super呢?如果不能自动调用父类的构造器,那么在重写__init__又得给所有的属性都分配一下,这个好麻烦的,该怎么办呢?只能以编程的方式自己调用了。

       super(类,instance),这个函数的作用是找到指定的类的下一个父类的指定方法,将该方法在指定的实例上调用。

         例如上面的例子中,继承关系是这样的:Student > Person > object。self是Student对象,super(Student, self).__init__(id, name, age)该语句的意思就是:找到Student的下一个父类(即Person)的__init__方法,然后在self上调用该__init__方法。

      需要注意的是:

      1super函数的两个参数,不能有误。需要满足两个条件,第二个instance应该是第一个参数所代表的类的实例(或者是其子类的实例)。

      2)Super只能在新式类中使用。

    4、运算符重写

      在.Net和Scala编程语言中,都是支持运算符重写的。如果没有接触过这些东西,可能会费解的。其实只要把运算符看着是方法就可以了。从这个角度来理解的话,Java也是支持运算符重写的,只不过呢,该特性并没有暴露给用户罢了。例如字符串拼接 + ,它其实是调用的StringBuilder.append方法,遍历集合的foreach,它其实调用的是iterator。既然提到了运算符重写,那么Python中也必然是支持的,并且它把这个特性暴露出来了。

      Python String 就重写了几个运算符:

    + 字符串拼接
    * copy多份
    == 比较字符串内容
    > 字符串比较
    < 字符串比较

      那么怎样实现运算符重写呢? 

      上面已经说明,将运算符看作是一个方法。那么重写运算符,就是就是重写方法了。

      下面列出了常见的运算符重载:

    重载方法

    说明

    调用

    __init__

    构造器

    对象建立,X=Class()

    __del__

    析构方法

    对象回收,或者del X

    __add__

    运算符 +

    X+Y, X+=Y

    __iadd__

    增强的 +

    X+Y, X+=Y

    __radd__

    左侧+

    Noninstance + Y

    __or__

    运算符 | (位 OR)

    X|Y, X|=Y

    __repr__,__str__

    打印,转换

    print(X), repr(X),str(X)

    __getattr__

    点号运算符

    获取属性

    __setattr__

    赋值运算符

    设置属性

    __call__

    函数调用

    Y()

    __len__

    长度

    len(X)

    __cmp__,

    __lt__,

    __eq__

    比较

    X < Y

    X==Y

    __getitem__

    索引运算符

    X[name]

    __setitem__

    索引赋值

    X[Y]=Z

    __iter__

    迭代

    循环,迭代等

      使用__getitem__可以使得对象具备索引的方式来访问?

      测试如下:对上面的Student如下:

     

    class Student(Person):
        def __init__(self, id, name, age,email):
            print("invoke in Student __init__")
            super(Student, self).__init__(id, name, age)
            self.email = email
    
    
        def __getitem__(self, name):
            return self.__dict__[name] + "_suffix"

      执行测试: 

      

    import model
    Student = model.Student
    s = Student('0001', 'fjn', 20, 'fs1194361820@163.com')
    s.show()
    print(s["email"])

      发现结果是:fs1194361820@163.com_suffix 

    5、模拟私有属性

      通过上面的学习,了解到Python的属性、方法都是public, 这点和JavaScript太 一样了。写Java时间久了,私有属性的好处,怎么可以浪费了呢。那么如何模拟出私有的?

    有两种方式可以模拟的,都与JavaScript里的理念是类似的。

      方式一:使用对象时,直接加属性。

      方式二:利用对象的__dict__

    6、Static Method

      在java中,会将实例方法与类方法区分,具体做法是在 class 方法上加上static修饰符。在python中是没有static的,那么如何实现static方法。

      前面也讲了,实例方法声明时,第一个参数是self。在python中,实例方法本质就是调用类方法的。  

    p1=Person(‘a’,’b’,23)
    Person.show(p1)

    在方法前加上注解:@staticmethod

  • 相关阅读:
    Leetcode Binary Tree Level Order Traversal
    Leetcode Symmetric Tree
    Leetcode Same Tree
    Leetcode Unique Paths
    Leetcode Populating Next Right Pointers in Each Node
    Leetcode Maximum Depth of Binary Tree
    Leetcode Minimum Path Sum
    Leetcode Merge Two Sorted Lists
    Leetcode Climbing Stairs
    Leetcode Triangle
  • 原文地址:https://www.cnblogs.com/f1194361820/p/8645913.html
Copyright © 2011-2022 走看看