• python:面向对象程序设计及property装饰器


    Python面向对象设计特点

    • 具有面向对象的所有特征:封装、继承、多态;
    • 所有类的父类为object;
    • 子类可以对父类的任何方法都可能进行重写;
    • Python中没有提供重载和访问控制,但是属性可以用特殊名进行访问控制

    定义类

    类定义格式如下:

    class A:
        pass
    class B(A):
        pass
    属性和方法

    在java和C++中,分别提供了this引用和this指针,表示当前的调用对象或指针,在Python中,则提供了类似的self参数,在对方法进行调用时,会自动提供self参数,但是必须在参数列表中包含该参数,因此,方法的定义格式如下:

    class A:
        def func(self):
            print("我是普通方法")

    有了self参数,类的属性就是通过self参数来存取,如:

    class A:
        def func(self,name,age):
            self.name = name
            self.age = age
            print("我是普通方法")    

    完整的类定义如下:

    class A:
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def func(self):
            print(666)
        def __eq__(self, other):
            return self.name == other.name and self.age == other.age
        def __repr__(self):
            return "Animal('{0}',{1}".format(self.name,self.age)
        def __str__(self):
            return "Animal({0},{1}".format(self.name,self.age)
    
    a1 = A('alex',1000)
    print(a1.name) #'alex"
    print(a1.func()) # 666  None
    print(a1.__eq__(a1)) # True
    print(a1.__str__()) #Animal(alex,1000
    print(a1.__repr__()) #Animal('alex',1000

    其中以_开头和结尾的方法都来自于基类object,当定义好类后,就可以了导入人类所在模块后使用了,

    如:

    import Module_A
    dog = Module_A
    dog.eat()

    如果想要定义一个私有的属性,则在属性前面加上__就变成私有的了

    如:

    class A:
        def __init__(self,name='alex',age=1000):
            self.__name=name
            self.age=age
    if __name__ == "__main__":
        dog = A()
        print(dog.__name)
        #AttributeError: 'A' object has no attribute '__name'
    基本特殊方法

    __new()____init()__方法: 
    和其他语言不同,Python中要创建一个对象,当调用dog = Module_Animal.Animal("Dog")时,执行了两个步骤:

    • 第一步,使用__new__()方法创建该对象;
    • 第二步,使用__init__()方法对其进行初始化;

    在创建类时,应该重写__init__()方法进行初始化,__new__()不必须进行重写。在创建对象时如果发现没有提供,则自动调用object.__new__()。 
    如果重写了__init__()方法并且想要调用基类该方法,则可以通过super()方法:

    def __init__(self):
        super().__init__()

    __eq__(self, other)__ne__(self, other)方法: 
    这两个方法用于两个对象之间进行比较,对于__eq__(),如果self和other相等,则返回True;对于__ne__(),如果self和other不等,则返回True,如:

    dog = Animal("Dog")
    dog.eat()
    cat = Animal("cat")
    cat.eat()
    print(dog == cat)   # return False
    isEqual = dog.__eq__(cat)
    isNotEqual = dog.__ne__(cat)
    print(isEqual) # return False
    print(isNotEqual)  # return True
    • 默认情况下,所有的自定义类都支持==进行比较,但总是返回False(除非dog == dog),因此可以通过__eq__()来实现比较操作。
    • 如果提供了__eq__()方法但没有提供__ne__()方法,Python会自动提供__ne__()!=操作符;如果没有提供__eq__(),调用结果为NotImplemented。
    • 默认情况下,自定义类的所有实例都是可哈希运算的,可以调用__hash__()方法,也可以作为字典的键,但是如果重写了__eq__()方法,则该类的实例就不是可哈希运算的了。
    • 应避免非同类型间进行比较,可用内置函数isInstance(obj,Obj)处理,第一个参数为对象,第二个为类型,如:
    def __eq__(self, other):
        if not isinstance(other,Animal):
            return NotImplementedError
        return self.name == other.name and self.age == other.age
    • 对于比较操作符,Python中都提供了特殊的方法: 
      • __le__(self,other):self <= other,返回Ture
      • __lt__(self,other):self < other,返回Ture
      • __ge__(self,other):self >= other,返回Ture
      • __gt__(self,other):self > other,返回Ture
      • __ne__(self,other):self != other,返回Ture
      • __eq__(self,other):self == other,返回Ture

    __repr__()__str__()方法: 
    调用内置repr()函数时,会调用给定对象的__repr__()方法,该方法用于返回一个特殊的字符串,该字符串可通过内置eval()函数生成一个Python对象,如:

    def __repr__(self):
        return "Animal('{0}',{1})".format(self.name,self.age)
    if __name__ == "__main__":
        cat = Animal("Cat")
        ani = eval(repr(cat))  # return Animal('Cat',0)
        print(ani)  # Animal(Cat,0)

    调用内置函数str()时,会调用给定对象的__str__()方法,该方法也会返回一个字符串,和__repr__()的区别主要是该方法产生的字符串主要是便于理解,而不是为了传递给eval()函数。 
    如:

    def __str__(self):
        return "Animal({0},{1})".format(self.name, self.age)
        print(str(ani))  # Animal(Cat,0)
    • 如果实现了__repr__()而没有实现__str__()时,则在调用str()、print(obj)时也会执行__repr__()方法。 
      __hash__()方法: 
      默认情况下,所有的类都是可以哈希运算的,但是如果类实现了__eq__()方法,那么则不可进行哈希运算,通过提供该方法可以使类能狗进行哈希运算,实现如下:
    def __hash__(self):
        return hash(id(self))

    内置hash()方法根据独一无二的ID来计算哈希值,内置id()方法返回一个独一无二的整数

    继承和多态

    Python中定义一个类继承另一个类格式如下:

    class Subclass(A):
        pass

    子类可以对父类的方法进行重写,如果子类想调用父类的方法,可以使用super()进行调用,如:

    class Animal:
        def __init__(self,name="Animal",age=0):
            self.__name = name
            self.age = age
    
        def eat(self):
            print('Animal is eating')
    
        def shout(self):
            print("Animal is shouting")
    
    
    class Dog(Animal):
        def __init__(self,name="Dog",age=0):
            # 调用Animal的__init__()方法
            super().__init__()
            self.name = name
            self.age = age
    
        def shout(self):
            print("Dog is Shouting")
    
        def eat(self):
            print("Dog is eating")
    
    class Cat(Animal):
        def __init__(self,name="Cat"):
            self.name = name
    
        def shout(self):
            print("Cat is shouting")
    
        def eat(self):
            print("Cat is eating")

    在java等面向对象语言中,多态是指子类的引用指向父类的实例,但是在Python中,可能不能用这种方式来理解,因为Python中定义一个变量或对象并不需要声明数据类型,因此对于Python而言,其多态的表现形式就是:如果子类对父类方法进行了覆盖,则子类对象调用该方法时,会调用子类对方法的实现。如:

    if __name__ == "__main__":
    
        ani = Animal()
        dog = Dog()
        cat = Cat()
        ani.eat()  # Animal is eating
        dog.eat()  # Dog is eating
        cat.eat()  # Cat is eating

    python提供了isinstance(o,Obj)方法,可以判断对象o是否是Obj类型,因此,在使用多态时,可以这样使用:

    # 定义一个函数
    def animal_eat(animal):
        animal.eat()
    
    if isinstance(dog,Animal):
        animal_eat(dog) # Dog is eating
    if isinstance(cat,Animal):
        animal_eat(cat) # Cat is eating
    
    

    property装饰器

    在面向对象的设计中,常常要将属性进行封装,提供setter/getter方法对属性进行操作,Python中也可以提供setter/getter进行对属性的封装,从而保证数据的安全性,但是并不推荐使用,因为有更优的方式可以属性的安全性,下面逐步进行分析。 
    现在定义一个类:

    import math
    
    class Circle:
    
        def __init__(self, radius, x=0, y=0):
            self.radius = radius
    
        def area(self):
            return math.pi * (self.radius ** 2)
    
    
    if __name__ == "__main__":
    
        c1 = Circle(2)
        c1.radius = -3
        print(c1.area())  
        print(c1.radius)  # -3

    可以看到,属性radius有可能被设置为不合理的值,因此,可以将radius设置为私有属性,同时提供setter/getter方法对其进行设置,修改该类实现如下:

    class Circle:
    
        def __init__(self, radius, x=0, y=0):
            if radius <= 0: 
                self.__radius = 1
            self.__radius = radius
    
        def set_radius(self, radius):
            assert radius >= 0, "radius can't be zero"
            self.__radius = radius
    
        def get_radius(self):
            return self.__radius
    
    
    if __name__ == "__main__":
    
        c1 = Circle(2)
        c1.set_radius(-3)  # AssertionError: radius can't be zero
        print(c1.area())
        print(c1.get_radius())  # 2

    通过setter/getter可以将属性封装起来,可以保证属性的安全性,但是这种方式比较繁琐,在Python中,更倾向于使用Property装饰器,使用方式如下:

    # class A:
    #     def __init__(self,name,age):
    #         self.name = name
    #         self.age = age
    #     def func(self):
    #         print(666)
    #     def __eq__(self, other):
    #         return self.name == other.name and self.age == other.age
    #     def __repr__(self):
    #         return "Animal('{0}',{1}".format(self.name,self.age)
    #     def __str__(self):
    #         return "Animal({0},{1}".format(self.name,self.age)
    #
    # a1 = A('alex',1000)
    # print(a1.name) #'alex"
    # print(a1.func()) # 666  None
    # print(a1.__eq__(a1)) # True
    # print(a1.__str__()) #Animal(alex,1000
    # print(a1.__repr__()) #Animal('alex',1000
    
    
    class A:
        def __init__(self,name='alex',age=1000):
            self.__name=name
            self.age=age
    if __name__ == "__main__":
        dog = A()
        print(dog.__name)
        #AttributeError: 'A' object has no attribute '__name'
    
    

    Property装饰器是一个函数,该函数以一个方法作为参数,并返回修饰后的版本。但是通常并不使用该方法,而是通过@符号来标记,如上例所示。property()是一个内置函数,至多可以接受四个参数:get参数、set参数、delete参数、docstring参数。@property相当于property(get)。 
    在上例中,创建了一个__radius私有属性,然后通过property装饰器进行其getter、setter的设置。需要注意:

    • getter和setter有同样的名称,如def radius(self);
    • 当使用@property创建特性后,每个创建的特性都包含getter、setter、deleter、docstring等属性,并且都是可用的。
    • getter为@property设置的方法,其他属性由python设置。

    当创建radius装饰器后,就可以通过radius.setter和radius.getter获取和设置__radius属性了,同时保证了私有属性的安全性,如:

    c1.radius = 0  # AssertionError: radius can't be zero
    c1.radius = 3  # 调用radius(self,radius),相当于radius.setter
    print(c1.radius)  # 2  调用radius.setter
  • 相关阅读:
    STL源码剖析:迭代器
    STL源码剖析:配置器
    [bzoj3940][Usaco2015 Feb]Censoring
    [bzoj2212][Poi2011]Tree Rotations
    [bzoj2733]永无乡&&[bzoj3545]Peaks
    挂个AC自动机
    [bzoj4237]稻草人
    莫比乌斯反演定理证明
    斜率优化dp学习
    备忘
  • 原文地址:https://www.cnblogs.com/while-number/p/9272763.html
Copyright © 2020-2023  润新知