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

  • 相关阅读:
    求某个数的位数公式
    ArrayList和lInkedList比较
    队列
    抽象数据结构-栈
    LinkedList的实现
    ArrayList的实现
    Iterator和List的一些问题
    SCHEMA约束
    DTD约束
    XML解析
  • 原文地址:https://www.cnblogs.com/f1194361820/p/8645913.html
Copyright © 2011-2022 走看看