zoukankan      html  css  js  c++  java
  • 【Python】解析Python中类的使用

    目录结构:

    contents structure [-]

    1.类的基本使用

    下面是类使用的一个简单案例,

    class person:
        "person 类的描述信息"
        def __init__(self,name="",age=0):
            self.name = name
            self.age = age
    
        def setName(self,name):
        '''设置名称'''
            self.name = name
    
        def getName(self):
        '''获取名称'''
            return self.name
    
        def setAge(self,age):
            self.age = age
    
        def getAge(self):
            return self.age
    
        def __del__(self):
            print("person (name:"+self.name+",age:"+str(self.age)+") is deleting")
    
    p = person("jame",12);
    #Output:jame
    print(p.getName())
    #效果和下面的调用方式是一样的
    #person.getName(p)
    
    #Output:12
    print(p.getAge())
    #效果和下面的调用方式是一样的
    #person.getAge(self)
    
    #Output: person 类的描述信息
    print(p.__doc__)
    
    #输出person类的帮助信息
    print(help(person))
    
    del p

    输出:

    jame
    12
    person (name:jame,age:12) is deleting
    person 类的描述信息
    Help on class person in module __main__:
    
    class person(builtins.object)
     |  person 类的描述信息
     |  
     |  Methods defined here:
     |  
    ...


    __init__函数是构造函数,在构造对象的时候会自动调用该函数。
    __del__函数是析构函数,在使用del删除目标对象时会自动调用该方法。

    Python中,类不可以定义多个构造方法,只能定义一个构造方法。所有成员实例函数的第一个参数都必须是self参数(也可以有非self参数的成员函数,下面会讲解)。python会自动创建成员属性,无需提前定义,例如self.name=name。

    类属性

    类属性的定义无需使用self参数,可以直接定义到类中。类属性可以直接通过类名调用。

    class Person:
        type = "哺乳类"
    
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
    p = Person("luyis",13)
    #通过类名调用类属性
    print(Person.type)
    #通过对象调用类属性
    print(p.type)

    静态方法

    静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,主要是一些逻辑属于类,但是和类本身没有交互,即在静态方法中,不会涉及到类中的方法和属性的操作。用 @staticmethod 装饰的不带 self 参数的方法,类的静态方法可以没有参数,可以直接使用类名调用。

    import time
    class TimeTest:
        def __init__(self):
            pass
    
        #静态方法
        @staticmethod
        def show_time():
            print(time.strftime("%H:%M:%S", time.localtime()))
    
    TimeTest.show_time()

    类方法

    类方法是将类本身作为对象进行操作的方法。默认有个 cls 参数,可以被类和对象调用,需要加上 @classmethod 装饰器。

    class ClassTest:
        num = 0
        def __init__(self,num):
            ClassTest.num = num
    
        #类方法
        @classmethod
        def showNum(cls):
            print(cls.num)
    
    
    c = ClassTest(20)
    #通过类直接调用
    ClassTest.showNum()
    #通过实例对象调用
    c.showNum()

    私有成员

    Python中的私有成员,不能在类外面直接访问。私有成员只需要在成员前面加上两条下划线(__)就表明该成员是私有的了。

    class Person:
        #__type是私有成员
        __type = "哺乳类"
    
        def __init__(self,name="",age=0,country="中国"):
        #__name,__age是私有成员
            self.__name = name
            self.__age = age
            self.__setcountry(country)
    
        def get_name(self):
            return self.__name
    
        def get_age(self):
            return self.__age
    
        def set_name(self,name):
            self.__name = name
    
        def set_age(self,age):
            self.__age = age
    
        #__setcountry是私有函数
        def __setcountry(self,cty):
            self.country = cty
        
        @classmethod
        def get_type(cls):
            return cls.__type
    
        @classmethod
        def set_type(cls,type):
            cls.__type = type
    
    p = Person("jame",21)
    print(p.get_name())
    print(p.get_age())
    print("--------------------")
    
    p.set_name("charlse")
    p.set_age(31)
    print(p.get_name())
    print(p.get_age())
    print("-------------------")
    
    print(Person.get_type())
    print("------------------")
    
    Person.set_type("灵长类")
    print(Person.get_type())

    输出:

    jame
    21
    --------------------
    charlse
    31
    -------------------
    哺乳类
    ------------------
    灵长类


    定义私有成员的格式:
    __xxx:私有成员,只有类对象自己能访问,子类对象不能直接访问到这个成员,

    注意:虽然不能在类外直接访问到类的私有成员,但在对象外部可以通过“ 对象名._类名__xxx ”这样的特殊方式来访问类的私有成员。应此,在Python中不存在严格意义上的私有成员。

    class Person:
        #__type是私有成员
        __type = "哺乳类"
    
        def __init__(self,name="",age=0):
        #__name,__age是私有成员
            self.__name = name
            self.__age = age
    
    
    p = Person("Emma",32);
    #直接访问私有成员的值
    print(p._Person__type)
    print(p._Person__name)
    print(p._Person__age)
    
    #直接修改私有成员的值
    p._Person__age = 33
    p._Person__name = "Angela"
    p._Person__type = "灵长类"
    
    print("-------------------")
    print(p._Person__type)
    print(p._Person__name)
    print(p._Person__age)

    输出:

    哺乳类
    Emma
    32
    -------------------
    灵长类
    Angela
    33

    2.专有方法

    Python除了自定义私有变量和方法外,还可以定义专有方法。专有方法是在特殊情况下或使用特殊语法时由python调用的,而不是像普通方法一样在代码中直接调用。看到形如__XXX__的变量或函数名时就需要注意下,这在python中是有特殊用途的,下面是Python中的部分专用方法:
    __init__ : 构造函数,在生成对象时调用
    __del__ : 析构函数,释放对象时使用
    __str__: 打印,转换,适合于展示给用户看的
    __repr__ : 打印,转换,适合开发者调试
    __setitem__ : 按照索引赋值
    __getitem__: 按照索引获取值
    __len__: 获得长度
    __call__: 函数调用
    __add__: 加运算
    __sub__: 减运算
    __mul__: 乘运算
    __div__: 除运算
    __mod__: 求余运算
    __pow__: 乘方
    __doc__: 说明文档信息

    上面笔者已经讲解过__init__和__del__函数的使用了,应此笔者不再这里重复缀述了。笔者接下来讲解其它的专有方法的用法:

    __str__,__repr__

    都是用于将对象转化为字符串的内置方法。
    但是 repr() 函数将对象转化为供解释器读取的形式,__str__只是覆盖了__repr__以得到更友好的用户显示。
    例如:

    >>> y = 'a string'
    >>> repr(y)
    "'a string'"
    >>> str(y)
    'a string'


    repr函数的返回字符串可以再次传递给eval函数。但是str函数的返回值传递给eval显然是不合适。

    >>> y == eval(repr(y))
    True
    >>> y == eval(str(y))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<string>", line 1
        a string
               ^
    SyntaxError: unexpected EOF while parsing



    repr返回的字符串应该是编译器能够再次解析的,str返回的字符串应该可以是易阅读的

    在知道了repr和str的区别后,我们就知道该如何自定义__repr__和__str__函数了。

    class person:
        def __init__(self,name="",age=0):
            self.__name = name
            self.__age = age
    
        def __str__(self):
            return "name:%s,age:%d"%(self.__name,self.__age)
    
        def __repr__(self):
            return "person(name='%s',age=%d)"%(self.__name,self.__age)
    
    p = person("jame",12)
    #Output: name:jame,age=12
    print(str(p))#same as print(p)
    
    #Output: person(name='jame',age=12)
    print(repr(p))
    
    p2 = eval(repr(p))   
    #Output: name:jame,age=12
    print(p2)

    __setitem__,__getitem__

    用于通过下标来设置和获取值,可以直接使用[]符来操作。

    class DicDecorate:
    
        def __init__(self,dic):
            self.__dic = dic
    
        def __getitem__(self,key):
            return self.__dic[key]
    
        def __setitem__(self,key,val):
            self.__dic[key] = val
    
    
    dicdec = DicDecorate({})
    
    dicdec[0] = "hello"
    dicdec[1] = "word"
    
    print(dicdec[0])
    print(dicdec[1])

    __len__():

    当调用len函数时会调用该方法。

    lass DicDecorate:
        def __init__(self,dic):
            self.__dic = dic
    
        def __len__(self):
            return len(self.__dic);
    
    dicdec = DicDecorate({})
    print(len(dicdec))

    __call__()

    关于 __call__ 方法,不得不先提到一个概念,就是可调用对象(callable),我们平时自定义的函数、内置函数和类都属于可调用对象,但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象,判断对象是否为可调用对象可以用函数 callable。

    如果在类中实现了 __call__ 方法,那么实例对象也将成为一个可调用对象。

    class Entity:
        def __init__(self, x, y):
            self.x, self.y = x, y
    
        def __call__(self, x, y):
            self.x, self.y = x, y
    
        def __str__(self):
            return "x=%s,y=%s"%(self.x,self.y)
    
    e = Entity(2, 3) # 创建实例
    if(callable(e)):
        e(4, 5) #实例可以象函数那样执行,并传入x y值,修改对象的x y
    #Output:x=4,y=5
    print(e)


    __add__: 加运算 __sub__: 减运算 __mul__: 乘运算 __div__: 除运算 __mod__: 求余运算 __pow__: 幂运算

    class Vector:
       def __init__(self, a, b):
          self.a = a
          self.b = b
    
       def __str__(self):
          return 'Vector (%d, %d)' % (self.a, self.b)
    
       def __add__(self,other):
          return Vector(self.a + other.a, self.b + other.b)
    
    v1 = Vector(2,10)
    v2 = Vector(5,-2)
    print (v1 + v2)

    3. 继承

    3.1 单重继承

    Python继承的语法格式:

    class ClassName1(ClassName2):
        statement

    其中
    ClassName1:派生类
    ClassName2:基类

    class Shape:
        def __init__(self,type,area):
            self.type = type
            self.area = area
        def describe(self):
            return "Share type:%s,area=%f"%(self.type,self.area)
    class Square(Shape):
        def __init__(self,area):
            super().__init__("square",area);
    square = Square(12.1)
    print(square.describe())

    和其它编程语言一样,可以重写父类中的方法,可以继承父类中开放的属性和方法(不能继承父类中的私有属性和方法)。

    3.2 多重继承

    除了单重继承,Python还支持多重继承

    class ClassName1(ClassName2,ClassName3,ClassName4...):
        statement

    Python多重继承中顺序继承是一个非常重要的概念,如果继承的多个父类中有相同的方法名,但在派生类中使用时未指定父类名,则Python解释器将从左至右搜索,即调用先继承的类中的同名方法。

    class A:
        def test(self):
            print("I am in Class A");
    class B:
        def test(self):
            print("I am in Class B");
    
    class C(A,B):
        def __call__(self):
        #调用A类中的test方法
            self.test()
            #可以通过类名显示指定调用B类中的test方法
            B.test(self)
    
    class D(B,A):
        def __call__(self):
        #调用B类的test方法
            self.test()
        #通过类名显示指定要调用A类中的test方法
        A.test(self)
    
    c = C()
    c()
    print("---------------");
    d = D()
    d()

    输出:

    I am in Class A
    I am in Class B
    -----------------
    I am in Class B
    I am in Class A

    通过上面的结果可以看出,子类在父类中查找方法的顺序是从左到右的。

    3.3 砖石继承

    砖石继承问题是Python多重继承中的典型问题,下面先通过一张图片看看什么是砖石继承。


    通过这张图片看出,D继承了B和C,B和C又派生于A,在调用D类的test()方法时,会再去调用B和C的test()方法,B和C又会去调用A的test()方法,所以A类的test()方法在理论应该会被调用两次,例如:

    class A:
        def test(self):
            print("I am in Class A");
    class B(A):
        def test(self):
            A.test(self);
            print("I am in Class B")
    class C(A):
        def test(self):
            A.test(self);
            print("I am in Class C");
            
    class D(B,C):
        def test(self):
            B.test(self)
            C.test(self)
            print("I am in Class D")
    
    d = D()
    d.test()

    输出:

    I am in Class A
    I am in Class B
    I am in Class A
    I am in Class C
    I am in Class D


    从上面的输出结果可以看出,A类的test()被调用了两次。在有些情况这种逻辑将会造成极大的Bug,比如一笔银行转账记录转了两次。要避免这种情况,可以使用super()方法,当使用super()调用父类的test方法时,会转而去调用下一个重写了该super类的test方法,若没有的话才会去调用父类的test方法。
    案例:

    class A:
        def test(self):
            print("I am in Class A")
    class B(A):
        def test(self):
            super().test() #不能替换为A.test(self)
            print("I am in Class B")
    class C(A):
        def test(self):
            super().test()
            print("I am in Class C")
            
    class D(B,C):
        def test(self):
            super().test() #可以替换为B.test(self)
            print("I am in Class D")
    
    d = D()
    d.test()

    输出:

    I am in Class A
    I am in Class C
    I am in Class B
    I am in Class D


    通过上面的结果我们可以看出,super.test()和父类.test(self)是不一样的,在B类中使用super()调用父类的test()时,会去寻找B实例后的下一个类是否重写A类的test()方法,由于类D继承了类B和类C,所以类C在类B后,而且类C又重写了test()方法,应此会直接调用类C的test()方法。在类C的test()方法中,又使用了super().test()调用父类的test(),所以又会去寻找C实例后的下一个类有重写A类的test方法,因为类D只继承了B和C,并且类C是最后一个,所以C后没有实例直接重写A类的test()方法,因此直接去调用A类的test()方法。最终完整的方法压栈顺序是D->B->C->A。

    下面的逻辑和上面案例中使用super()的逻辑是等同的:

    class A:
        def test(self):
            print("I am in Class A")
    class B(A):
        def test(self):
        C.test(self)#调用C
    class C(A):
        def test(self):
            A.test(self)#调用A
    class D(B,C):
        def test(self):
            B.test(self)#调用B

    上面的逻辑变成了如图所示:




  • 相关阅读:
    ISEX安全期计算 V4.0 多国语言版女性安全期避孕计算实用工具软件
    FireARP防火墙 V3.1防局域网ARP攻击的软件
    asp.net页面字段绑定概述
    C# 删除、遍历指定目录下的所有指定文件、文件夹
    加入收藏、设为首页常用脚本
    分页加载数据Demo
    学习fragment的生命周期
    widget控件
    Menu菜单的代码和xml的创建
    WebView控件
  • 原文地址:https://www.cnblogs.com/HDK2016/p/10964792.html
Copyright © 2011-2022 走看看