• Python3之面向对象


    一、定义:面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)

    • 面向过程:根据业务逻辑从上到下写垒代码
    • 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可
    • 面向对象:对函数进行分类和封装,让开发“更快更好更强...”

      1.创建类和对象:面向对象编程是一种编程方式,需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。

      

    class 类名:
        静态属性1 = '静态属性1'              # 公开静态属性 只有是这个类的就有这个属性
        __静态私有属性2 = '__静态属性2'       # 私有静态属性 只有是这个类的就有这个属性
        def __init__(self):       # 初始化方法 self是对象,是一个必须传的参数
            self.name = 'felix'   #公开对象属性
            self.__age = 18      #私有对象属性
    
        def func1(self):        #类的动态公开方法 一般情况下必须传self参数
            pass
    
        def __func2(self):     #类的动态私有方法 一般情况下必须传self参数
            pass
    
    对象 = 类名()   #类的实例化  创建对象
    对象.静态属性1   #引用类的静态属性1
    对象.func1()

      说明:

    • 关键字:class  使用关键字class 来创建一个类
    • 关键字:self   一般情况,必须传的一个参数,代表类本身
    • 创建对象:类名称后加括号即可
    • 类的公开属性和方法可以在外部调用
    • 类的私有属性和方法不能在类的外部调用,只能在类的内部调用
      import math
      class Circle:
          def __init__(self,r):
              self.r = r
          def area(self):
              return math.pi*(self.r**2)
          def perimeter(self):
              return 2*math.pi*self.r
      A = Circle(5)
      print(A.area())
      print(A.perimeter())

      2.静态属性  就是直接在类中定义的变量

      (1)类中的静态变量,可以被对象调用
      (2)对于不可变数据类型来说,类变量最好用类操作
      (3)对于可变数据类型来说,对象名的修改时共享的,重新赋值是独立的

      3.动态属性 就是类中定义的方法(函数)

      4.命名空间:类和对象分别存在不同的命名空间中

      4.组合:一个对象的属性值是另外一个类的对象

    import math
    class Circle:
        def __init__(self,r):
            self.r = r
        def area(self):
            return math.pi*(self.r**2)
        def perimeter(self):
            return 2*math.pi*self.r
    
    class Ring:
        def __init__(self,outside_r,inside_r):
            self.outside_r = Circle(outside_r)
            self.inside_r = Circle(inside_r)
    
        def area(self):
            return self.outside_r.area() - self.inside_r.area()
        def perimeter(self):
            return self.outside_r.perimeter() + self.inside_r.perimeter()
    R = Ring(20,10)
    print(R.area())
    print(R.perimeter())
    

    、三大特性之封装

      封装就是在变量的左边加上双下划线,有以下三种情况:

    1.类中的静态私有属性

    2.类中的私有方法

    3.对象的私有属性

    所有的私有属性和方法都不能在类的外部调用。

    class Person:
        __key = 123  # 私有静态属性
        def __init__(self,name,passwd):
            self.name = name
            self.__passwd = passwd   # 私有属性
    
        def __get_pwd(self):         # 私有方法
            return self.__passwd   #只要在类的内部使用私有属性,就会自动的带上_类名
    
        def login(self):          # 正常的方法调用私有的方法
            self.__get_pwd()
    
    felix = Person('felix','felix666')
    print(felix._Person__passwd)   # _类名__属性名
    print(felix.get_pwd())  #报错,类外部不能调用私有属性
    
    执行结果:
    felix666
    AttributeError: 'Person' object has no attribute 'get_pwd'
    

      

    四、三大特性之继承

      1.定义

    继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。

      2.单继承和多继承

    class ParentClass1: #定义父类(基类、超类)
        pass
    
    class ParentClass2: #定义父类(基类、超类)
        pass
    
    class SubClass1(ParentClass1): #Subclass是子类(派生类)   单继承 ,基类是ParentClass1,派生类是SubClass
        pass
    
    class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
        pass

      实例:

    class Animal:  #父类  基类  超类
        def __init__(self):
            print('执行Animal.__init__')
            self.func()
        def eat(self):
            print('%s eating'%self.name)
        def drink(self):
            print('%s drinking'%self.name)
        def func(self):
            print('Animal.func')
    
    class Dog(Animal):  #子类 派生类
        def guard(self): #派生方法
            print('guarding')
        def func(self):
            print('Dog.func')
    dog = Dog()
    • 父类中没有的属性,在子类中出现 叫做派生属性
    • 父类中没有的方法,在子类中出现 叫做派生方法
    • 只要是子类的对象调用,子类中有名字的,一定用子类的;子类中没有才找父类的,如果父类也没有就报错
    • 如果父类、子类都有的属性和方法,就使用子类的;如果还想用父类的(单独调用父类的),   调用方法:父类名.方法名(需要自己传self参数)

          调用方法:父类名.方法名(self)   #需要传自己的self参数

                 super().方法名           #不需要自己传self  python3

      3.继承顺序

       那么问题又来了,多继承呢?

    • 是否可以继承多个类
    • 如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?
    class F:
        def func(self):
            print('F')
    class D(F):
        def func(self):
            super().func()
            print('D')
    
    class E(F):
        def func(self):
            super().func()
            print('E')
    
    class B(D):
        def func(self):
            super().func()
            print('B')
    
    class C(E):
        def func(self):
            super().func()
            print('C')
    
    class A(B,C):
        def func(self):
            super().func()
            print('A')
    
    a = A()
    a.func()
    print(A.mro())
    执行结果:
    F
    E
    C
    D
    B
    A
    [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>]

    新式类和经典类
    关于多继承寻找父类的顺序:新式类广度优先 经典类深度优先
    新式类中 有一个类名.mro方法 查看广度优先的继承顺序
    在Python3中,有一个super方法,根据广度优先的继承顺序查找上一个类

      

    • Python的类可以继承多个类,Java和C#中则只能继承一个类
    • Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先(py3)

      4.接口类和抽象类

    • 接口类和抽象类都是面向对象的规范,所有的接口类和抽象类都不能实例化
    • 接口类是支持多继承的,抽象类不支持多继承

      (1)接口类

     接口类的单继承:

    class Wechat(Payment):
        def pay(self,money):
            print('已经用微信支付了%s元'%money)
    
    class Alipay(Payment):
        def pay(self,money):
            print('已经用支付宝支付了%s元' % money)
    
    class Applepay(Payment):
        def pay(self,money):
            print('已经用applepay支付了%s元' % money)
    
    def pay(pay_obj,money):  # 统一支付入口
        pay_obj.pay(money)
    
    # wechat = Wechat()
    # ali = Alipay()
    app = Applepay()
    # wechat.pay(100)
    # ali.pay(200)
    p = Payment()
    

     接口类的多继承:

    # Tiger 走路 游泳
    # Swan 走路 游泳 飞
    # Eagle 走路 飞
    from abc import abstractmethod,ABCMeta
    class Swim_Animal(metaclass=ABCMeta):
        @abstractmethod
        def swim(self):pass
    
    class Walk_Animal(metaclass=ABCMeta):
        @abstractmethod
        def walk(self):pass
    
    class Fly_Animal(metaclass=ABCMeta):
        @abstractmethod
        def fly(self):pass
    
    class Tiger(Walk_Animal,Swim_Animal):
        def walk(self):
            pass
        def swim(self):
            pass
    class Eagle(Fly_Animal,Walk_Animal):
        def fly(self):
            pass
        def walk(self):
            pass
    class Swan(Swim_Animal,Walk_Animal,Fly_Animal):
        def walk(self):
            pass
        def swim(self):
            pass
        def fly(self):
            pass
    
    t = Tiger()
    e = Eagle()
    s = Swan()
    

      

      (2)抽象类

    import abc #利用abc模块实现抽象类
    
    class All_file(metaclass=abc.ABCMeta):
        all_type='file'
        @abc.abstractmethod #定义抽象方法,无需实现功能
        def read(self):
            '子类必须定义读功能'
            with open('filaname') as f:
                pass
    
        @abc.abstractmethod #定义抽象方法,无需实现功能
        def write(self):
            '子类必须定义写功能'
            pass
    
    class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('文本数据的读取方法')
        def write(self):
            print('文本数据的读取方法')
    
    class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('硬盘数据的读取方法')
    
        def write(self):
            print('硬盘数据的读取方法')
    
    class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('进程数据的读取方法')
    
        def write(self):
            print('进程数据的读取方法')
    
    wenbenwenjian=Txt()
    
    yingpanwenjian=Sata()
    
    jinchengwenjian=Process()
    
    #这样大家都是被归一化了,也就是一切皆文件的思想
    wenbenwenjian.read()
    yingpanwenjian.write()
    jinchengwenjian.read()
    
    print(wenbenwenjian.all_type)
    print(yingpanwenjian.all_type)
    print(jinchengwenjian.all_type)
    

      

    小结:


     # java :

    java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题

    但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题

    # python

    python中没有接口类概念,但是直接用多继承,实现了接口类  abc模块中的meraclass=ABCMeta @abstructmethod

    python中支持抽象类 : 一般情况下 单继承 不能实例化

    Python 支持单继承也支持多继承 所以对于接口类和抽象类的区别就那么明显了


    、三大特性之多态

      Pyhon不支持Java和C#这一类强类型语言中多态的写法,但是原生多态,其Python崇尚“鸭子类型”。

      如果两个类刚好相似,并不产生父类的子类的兄弟关系,而是鸭子类型 即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’

     

    优点 : 松耦合 每个相似的类之间都没有影响
    缺点 : 太随意了,只能靠自觉
    在面向对象方法中一般是这样表述多态性:
    向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。
    也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。
    
    比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同

     多态性:

    peo=People()
    dog=Dog()
    pig=Pig()
    
    #peo、dog、pig都是动物,只要是动物肯定有talk方法
    #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
    peo.talk()
    dog.talk()
    pig.talk()
    
    #更进一步,我们可以定义一个统一的接口来使用
    def func(obj):
        obj.talk()
    
    #二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
    class TxtFile:
        def read(self):
            pass
    
        def write(self):
            pass
    
    class DiskFile:
        def read(self):
            pass
        def write(self):
            pass
    

    六、类的装饰器

      类的内置函数

      1.@property   作用是在新式类中返回属性值。

    实例1:

    import math
        class Circle:
            def __init__(self,r):
                self.r = r
            @property
            def perimeter(self):  #用了类的装饰器 property 后,不能在此处加参数
                return 2 * math.pi * self.r
            @property
            def area(self):   #用了类的装饰器 property 后,不能在此处加参数
                return math.pi * (self.r ** 2)
     
    c1 = Circle(5)
    print(c1.area)   #用了 类的property装饰器后 area 变成了 c1的属性了
    print(c1.perimeter)   #用了 类的property装饰器后 perimeter 变成了 c1的属性了

    实例2:

    class Person:
        def __init__(self,name):
            self.__name = name
        @property
        def name(self):
            return self.__name + '牛牛牛'
        @name.setter
        def name(self,new_name):
            self.__name = new_name
    
    felix = Person('felix')
    print(felix.name)
    felix.name = 'all'
    print(felix.name)
    

      2.@classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。

    实例1:

    class A(object):
        bar = 1
        def func1(self):  
            print ('foo') 
        @classmethod
        def func2(cls):
            print ('func2')
            print (cls.bar)
            cls().func1()   # 调用 foo 方法
     
    A.func2()               # 不需要实例化
    
    输出结果为:
    
    func2
    1
    foo
    

      

    实例2:

    class A(object):
    
        # 属性默认为类属性(可以给直接被类本身调用)
        num = "类属性"
    
        # 实例化方法(必须实例化类之后才能被调用)
        def func1(self): # self : 表示实例化类后的地址id
            print("func1")
            print(self)
    
        # 类方法(不需要实例化类就可以被类本身调用)
        @classmethod
        def func2(cls):  # cls : 表示没用被实例化的类本身
            print("func2")
            print(cls)
            print(cls.num)
            cls().func1()
    
        # 不传递传递默认self参数的方法(该方法也是可以直接被类调用的,但是这样做不标准)
        def func3():
            print("func3")
            print(A.num) # 属性是可以直接用类本身调用的
        
    # A.func1() 这样调用是会报错:因为func1()调用时需要默认传递实例化类后的地址id参数,如果不实例化类是无法调用的
    A.func2()
    A.func3()
    

      

    实例3:

    class Goods:
        __discount = 0.8
        def __init__(self,name,price):
            self.name = name
            self.__price = price
        @property
        def price(self):
            return self.__price * Goods.__discount
    
        @classmethod  #把一个方法 变成一个类中的方法 这个方法就直接可以被类调用,不需要任何依赖
        def change_discount(cls,new_diccount):  #修改折扣
             cls.__discount = new_diccount
    apple = Goods('苹果',5)
    print(apple.price)
    apple.change_discount(0.5)
    print(apple.price)
    执行结果:
    4.0
    2.5
    

      

      3.@staticmethod  返回函数的静态方法。

    该方法不强制要求传递参数,如下声明一个静态方法:

    class C(object):
        @staticmethod
        def f(arg1, arg2, ...):
            ...
    

    以上实例声明了静态方法 f,类可以不用实例化就可以调用该方法 C.f(),当然也可以实例化后调用 C().f()。

    class C(object):
        @staticmethod
        def f():
            print('runoob');
     
    C.f();          # 静态方法无需实例化
    cobj = C()
    cobj.f()        # 也可以实例化后调用
    

      

    class Login:
        def __init__(self,name,password):
            self.name = name
            self.pwd = password
        def login(self):pass
    
        @staticmethod
        def get_usr_pwd():   # 静态方法  此处没有参数
            usr = input('用户名 :')
            pwd = input('密码 :')
            Login(usr,pwd)
    
    Login.get_usr_pwd()
    
    在完全面向对象的程序中,如果一个函数即和对象没有关系 也和类也没有关系,那么就用staticmethod将这个函数变成一个静态方法
    

     

    七、反射

       python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射),是用字符串类型的名字去操作变量。

      反射使用的内置函数: hasattr、 getattr、 setattr、 delattr

      hasattr和getattr成对出现

      

    class A:
        def fun(self):
        print('in func')
    
    a = A()
    a.name = 'felix'
    a.age = 30
    
    1.反射对象的属性 getattr
        ret = getattr(a,'name') #通过变量的字符串形式取到的值
        print(ret)
        print(a.__dict__)
        变量名 = input('>>>')  #func
        print(getattr(a,变量名))
        print(a.__dict__[变量名])
    
    2.反射对象的方法 getattr
        ret = getattr(a,'func')
        ret()
    
    3.反射类的属性 getattr
    class A: price = 20 @classmethod def fun(self): print('in func') print(getattr(A,'price')) 4.反射类的方法 getattr # A.fun() if hasattr(A,'fun'): getattr(A,'fun')()

      

    # 模块
        内置模块也能用反射
    
            import time
            print(time.strftime('%Y-%m-%d %H:%M:%S'))
            print(getattr(time,'strftime')('%Y-%m-%d %H:%M:%S'))
    
        import mokuai
        # 反射模块的属性
    
        # 普通方法
        print(mokuai.day)
        # getattr方法
        print(getattr(mokuai,'day'))
    
        #反射模块的方法
        print(getattr(mokuai,'func')())
    
        import sys
        def func():
            print('反射自己模块的变量')
    
        name = 'felix'
        print(sys.modules)  #所有引用的模块
        print(sys.modules['__main__'])  #当前模块
        print(sys.modules['__main__'].name) #  #当前模块的变量 name
    
        # 反射自己模块中的变量
        print(getattr(sys.modules['__main__'],'name'))
    
        # 反射自己模块中的方法(函数)
        getattr(sys.modules['__main__'],'func')()
        getattr(sys.modules[__name__],'func')()
    
        # x = input('>>>')
        # print(getattr(sys.modules['__main__'],x))
    

      

    # setattr()  设置一个变量
    class A:
        pass
    a = A()
    setattr(A,'name','felix')  #设置类的属性
    setattr(a,'name','alina')  #设置对象的属性
    print(A.name) 
    print(a.name)
    felix
    alina # delattr() 删除一个变量 delattr(a,'name') #删除对象的属性 a就去找类的属性 为 felix print(a.name) felix delattr(A,'name') print(a.name) 报错: AttributeError: 'A' object has no attribute 'name'

      

    # setattr()  设置一个变量
    class A:
        pass
    a = A()
    setattr(A,'name','felix')  #设置类的属性
    setattr(a,'name','alina')  #设置对象的属性
    print(A.name) 
    print(a.name)
    felix
    alina
    
    # delattr()  删除一个变量
    delattr(a,'name') #删除对象的属性 a就去找类的属性 为 felix
    print(a.name)
        felix
     
    delattr(A,'name')
    print(a.name)
        报错:
    AttributeError: 'A' object has no attribute 'name'
    

    八、类的内置方法(专有方法)

    1.__str__  --->>>       str(obj)

    2.__repr__  --->>>  repr(obj)

    class Teacher:
        def __init__(self, name, salary):
            self.name = name
            self.salary = salary
    
        def __str__(self):
            return "Teacher's object :%s" % self.name
    
        def __repr__(self):
            return str(self.__dict__)
    
        def func(self):
            return 'wahaha'
    
    felix = Teacher('felix', 8888)
    print(felix)  # 打印一个对象的时候,就是调用a.__str__
    print(str(felix))
    print(repr(felix))
    print('>>> %r' % felix)
    print(felix.__str__)
    执行结果:
    Teacher's object :felix
    Teacher's object :felix
    {'name': 'felix', 'salary': 8888}
    >>> {'name': 'felix', 'salary': 8888}
    <bound method Teacher.__str__ of {'name': 'felix', 'salary': 8888}>
    

     print(felix.__str__)

    object  里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址

    %s str() 直接打印 实际上都是走的__str__
    %r repr() 实际上都是走的__repr__

    repr 是str的备胎,但str不能做repr的备胎

    小结:
    1.print(obj) / '%s'%obj / str(obj)的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串
    如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。
    2.repr(),只会找__repr__,如果没有就找父类的

    3.__len__  --->>>        len()

    class Classes:
        def __init__(self,name):
            self.name = name
            self.student = []
        def __len__(self):
            return len(self.student)
    
    python = Classes('Python3')
    python.student.append('Felix')
    python.student.append('Alina')
    print(len(python))
    执行结果:
    2

    4.__del__      --->>>       析构函数

    class A:
        def __del__(self):  # 析构函数: 在删除一个对象之前进行一些收尾工作
            self.f.close()
    
    a = A()
    a.f = open('文件名')  # 打开文件 第一 在操作系统中打开了一个文件 拿到了文件操作符存在了内存中
    del a  # a.f 拿到了文件操作符消失在了内存中
    del a  # del 既执行了这个方法,又删除了变量
    

      

    5.__call__      --->>>       obj(obj) 

    class A:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __call__(self):
            for k in self.__dict__:
                print(k, self.__dict__[k])
    
    a = A('felix', 18)()
    执行结果:
    name felix
    age 18
    

    6.item

    1.__getitem__
    2.__setitem__
    3.__delitem__

    实例:
    class Teacher:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __getitem__(self, item):
            return self.__dict__[item]
    
        def __setitem__(self, key, value):
            self.__dict__[key] = value
    
        def __delitem__(self, key):
            # del self.__dict__[key]
            self.__dict__.pop(key)
    
        def __delattr__(self, item):  #
            self.__dict__.pop(item)
    
    felix = Teacher('felix',18)
    print(felix.__dict__['name'])
    
    felix.__dict__['sex'] = 'boy'
    print(felix.sex,felix.__dict__['sex'])
    print(felix.__dict__)
    
    # del felix.age  #object里的方法  原生支持 执行__delattr__方法 没有写__delattr__也不会报错
                     # 所以当没有__delitem__方法也不会报错 继承object
    del felix['age']  #当没有实现__delitem__方法会报错
    # print(felix.__dict__['age'])   #会报错
    print(felix.__dict__)
    
    执行结果:
    felix
    boy boy
    {'name': 'felix', 'age': 18, 'sex': 'boy'}
    {'name': 'felix', 'sex': 'boy'}
    

      

      

      

      

  • 相关阅读:
    java基本类型和包装类型的区别以及object的公共方法
    Scrapy学习
    centos系统python2.7更新到3.5
    requests和BeautifulSoup模块的使用
    基于角色的权限控制系统(role-based access control)
    Git的使用
    可插拔式后台管理系统(Django)
    Django admin site应用
    【算法】RMQ LCA 讲课杂记
    oh-my-zsh 安装和使用
  • 原文地址:https://www.cnblogs.com/Felix-DoubleKing/p/9788658.html
Copyright © 2020-2023  润新知