zoukankan      html  css  js  c++  java
  • Python之面向对象

    一、⾯向对象 和 ⾯向过程

    对于⼤型程序,其逻辑和功能都很复杂,如果 按照业务逻辑 去想,我们往往⽆从下⼿。

    ⾯向对象编程 (OOP,Object Oriented Programming)可以理解为 将具有相互关系的数据/操作封装成对象,以对象的⻆度去处理问题,让对象来完成相应处理

    按照业务逻辑 编写五⼦棋程序,过程应该是这样的:

    1.先显示棋盘() 
    2.执⿊棋⼿下⼦() 
    3.接着刷新界⾯() 
    4.然后判断⿊⼦是否胜出() 
    5.执⽩棋⼿下⼦() 
    6.接着刷新界⾯() 
    7.然后判断⽩⼦是否胜出() 
    8.重复2-7

    按照业务逻辑 从上到下 设计程序 的⽅式,叫做 ⾯向过程编程(Procedure Oriented Programming,POP,⾯向过程程序设计)

    ⾯向过程编程 最易被初学者接受,其往往⽤⼀⻓段代码来实现指定功能,⼀步接⼀步,环环相扣

    对于⼤型项⽬,如果使⽤⾯向过程编程,则很容易导致 代码结构过于紧密、耦合度⾼,易出现代码冗余,并且 不利于团队开发

    以对象的视⻆ 编写五⼦棋程序,过程是这样的:

    1. 根据 数据和操作 抽象出对象: 棋⼿ 棋盘 计算系统 
    2. 各对象实现⾃⼰的功能: 棋⼿.下⼦ 棋盘.刷新界⾯ 计算系统.判断胜负 .. 
    3. 使⽤对象执⾏操作 来完成业务逻辑: 
    棋盘.刷新界⾯ 
    棋⼿.下⼦ 
    计算系统.判断胜负 ...

    ⾯向对象编程 的优点

    将数据和业务抽象为 对象,有利于程序整体结构的分析和设计,使设计思路更加清晰

    业务以对象为单位,对象各⾃完成⼯作,减少了代码耦合度,有助于业务升级 和 代码重构

    ⽅便⼯作划分,有利于提⾼团队开发的效率

    二、类和对象

    对象的组成

    对象中包含两个组成部分:

    属性: ⽤于记录与对象相关的数据,⽐如姓名,年龄,身⾼,肤⾊等

    ⽅法: ⽤于实现与对象相关的操作,⽐如吃饭,睡觉,⻜⾏,歌唱等

    描述共同⾏为的集合,称为 类 (class)

    很多事物存在 相同的操作/⾏为,⽐如⼈都进⾏吃饭、睡觉,狗都会跑会叫等等

    类是总结事物特征的 抽象概念,⽽对象是 具体存在的某个实物

    类和对象的关系

    在编程中,类就是创建对象的模板 或者说 制造⼿册,⽤来定义对象公共的⾏为

    类总结了对象的共同特征,有利于复⽤代码创建拥有相同特征的对象

    每个对象必须有⼀个对应的类

    三、定义类和定义方法

    定义类

    定义⼀个类,格式如下:

    class 类名: 
        ⽅法列表

    类名 的命名规则按照"⼤驼峰"

    定义方法

    类是定义对象的共同⾏为的,也就是说 在类中定义对象的⽅法

    定义⽅法:

    class 类名: 
        def ⽅法名(self): 
        ...

    ⽅法的格式和函数类似,也可以设置参数和返回值,但是 需要设置第⼀个参数为 self

    注意定义⽅法需要在类的缩进中

    demo:定义⼀个Car类

    # 定义类 
    class Cat: 
        # ⽅法 
        def eat(self): 
            print("猫在吃⻥....") 
        def drink(self): 
            print("猫在喝可乐...")

    四、创建对象

    python中,可以根据已经定义的类去创建出对象

    创建对象的格式为:

    引⽤对象的变量名 = 类名()

    创建对象demo:

    # 根据类,创建⼀个对象 
    tom = Cat()

    调用方法:

    引⽤对象的变量名.⽅法名()

    注意:虽然定义⽅法时设置第⼀个参数 self ,但是 调⽤⽅法时不要传递对应 self 的参数

    # 创建了⼀个对象 
    tom = Cat() 
    tom.eat() # 调⽤对象的eat⽅法
    tom.drink()

    定义/使⽤属性

    定义/设置属性 格式:

    引⽤对象的变量名.属性名 = 数据

    属性和变量类似,⾸次赋值时会定义属性

    # 创建对象 
    tom = Cat() 
    # ⾸次赋值属性,会定义属性 
    tom.age = 3 
    # 定义属性后,再赋值属性,会修改属性保存的数据 
    tom.age = 1 
    # 获取属性值并打印 
    print(tom.age)

     第一次给对象的属性赋值就是定义一个属性,再次赋值只是修改属性记录的值

    五、self关键字

    关键字 self 主要⽤于对象⽅法中,表示调⽤该⽅法的对象。

    在⽅法中使⽤ self ,可以获取到调⽤当前⽅法的对象,进⽽获取到该对象的属性和⽅法

    某个对象调⽤其⽅法时,python解释器会把这个对象作为第⼀个参数传递给⽅法,所以开发者只需要在定义⽅法时 “预留” 第⼀个参数为 self即可

    class Dog:
        def eat(self):
            print("%s在吃东西" % self.name)
    dog1 = Dog()
    dog1.name = "旺财"
    dog1.eat()
    dog2= Dog()
    dog2.name = "来福"
    dog2.eat()

    结果:

    旺财在吃东西
    来福在吃东西

     六、__init__()⽅法定义属性

    在Python中,__xx__() 双下划线的方法一般叫魔法方法(运算符重载方法),都具有特殊含义,并且在特定的情况下自动调用。

    __init__() ⽅法叫做 对象的初始化⽅法,主要用于对象的初始化操作,如定义属性。在 创建⼀个对象后默认会被调⽤,不需要⼿动调⽤

    class Dog:
        def __init__(self):
            self.type = "小型动物"
        def eat(self):
            print("%s在吃东西" % self.name)
    dog1 = Dog() # 给对象开辟内存空间
    print(dog1.type)

    结果:小型动物

    __init__() ⾃定义参数

    __init__(self) 除了默认参数 self ,还可以设置任意个数的⾃定义参数,例如 __init__(self,x,y,z)

    init⽅法 设置的⾃定义参数必须和创建对象时传递的参数保持⼀致,例如 tom = Cat(x,y,z)

    开发者可以 设置⾃定义参数,为对象的默认属性提供 不同的初始值

    开发中,⼀般会在 __init__() 中定义对象的属性

    开发者可以 实现这个⽅法,并在该⽅法中定义属性并设置初始值

    想要自定义属性的初始值,可以设置init的自定义参数。

    init自定义参数对应的实参在创建对象的圆括号里边传递。

    class Dog:
        def __init__(self,name): # 给自定义方法添加相应的形参,叫自定义构造方法
            self.type = "小型动物"
            self.name = name # 自定义对象属性的初始值
        def eat(self):
            print("%s在吃东西" % self.name)
    dog1 = Dog("旺财")
    print(dog1.name)
    dog2 = Dog("来福")
    print(dog2.name)

     七、__str__()⽅法

    打印dog1对象时,输出的是dog1的地址

    class Dog:
        def __init__(self,name): # 给自定义方法添加相应的形参,叫自定义构造方法
            self.type = "小型动物"
            self.name = name # 自定义对象属性的初始值
        def eat(self):
            print("%s在吃东西" % self.name)
    dog1 = Dog("旺财")
    print(dog1.name)
    print(dog1)
    dog2 = Dog("来福")
    print(dog2.name)
    print(dog2)

    结果

    旺财
    <__main__.Dog object at 0x000002A89F753FD0>
    来福
    <__main__.Dog object at 0x000002A89F753F10>

    如果我们想输出某些特定的内容,则需要使用 __str__方法

    class Dog:
        def __init__(self,name): # 给自定义方法添加相应的形参,叫自定义构造方法
            self.type = "小型动物"
            self.name = name # 自定义对象属性的初始值
        def eat(self):
            print("%s在吃东西" % self.name)
        def __str__(self):
            return "我是%s"%(self.name)
    dog1 = Dog("旺财")
    print(dog1.name)
    print(dog1)
    dog2 = Dog("来福")
    print(dog2.name)
    print(dog2)

    结果

    旺财
    我是旺财
    来福
    我是来福

    print输出对象时,会自动调用 __str__,return的内容会被输出。

    如果没有定义__str__方法,直接print打印对象,会看到创建出来的对象在内存中的地址,只要对象定义了 __str__(self) ⽅法,就会 打印该⽅法return的信息描述

     八、私有属性

    定义set方法用来赋值,定义get方法用来取值。

    class Dog:
        def __init__(self): # 给自定义方法添加相应的形参,叫自定义构造方法
            self.age = None
        def setAge(self,age):
            if age > 0:
                self.age = age
        def getAge(self):
            return self.age
    dog1 = Dog()
    dog1.setAge(-10) # set方法赋值
    print(dog1.getAge()) # get方法取值
    dog1.setAge(10)
    print(dog1.getAge())
    dog1.age = -9
    print(dog1.getAge())

     以上代码虽然避免了脏数据,但是外部仍然可以访问age属性。此时我们需要将age属性私有化

    class Dog:
        def __init__(self): # 给自定义方法添加相应的形参,叫自定义构造方法
            self.__age = None
        def setAge(self,age):
            if age > 0:
                self.__age = age
        def getAge(self):
            return self.__age
    dog1 = Dog()
    dog1.setAge(-10)
    print(dog1.getAge())
    dog1.setAge(10)
    print(dog1.getAge())
    dog1.age = -9
    print(dog1.getAge())

    结果:

    None
    10
    10 #外部设置值未成功

    私有属性只能在类的内部访问,格式:

    属性名前加双下划线,如__age

    属性私有化之前,需要提供get和set方法来让外部通过调用方法的形式间接的访问私有属性。定义一对get和set方法,在set方法中对数据进行安全判断后再使用。

    为了避免属性被设置为 脏数据,更好的保护属性安全,⼀般的处理⽅式为

      1)、添加⼀个set⽅法,在⽅法内部先判断数据的有效性,再赋值属性

      2)、将属性定义为 私有属性,避免对象在外部直接操作属性

    如果在属性名前⾯加了2个下划线'__',则表明该属性是私有属性,否则为公有属性

    私有属性只能在类的内部访问

    九、私有方法

    私有方法和私有属性类似,也是只能在类的内部使用,在方法名字前面加双下划线。

    class Dog:
        def __init__(self): # 给自定义方法添加相应的形参,叫自定义构造方法
            self.__age = None
        def setAge(self,age):
            if age > 0:
                self.__age = age
            else:
                self.__info("age")
        def getAge(self):
            return self.__age
        def __info(self,property_name):
            print("%s属性赋值不成功" % property_name)
    dog1 = Dog()
    dog1.setAge(-10)
    print(dog1.getAge())
    dog1.setAge(10)
    print(dog1.getAge())

    私有⽅法和私有属性的 设计⽬的主要有两个:

      1)、保护数据或操作的安全性

      2)、向使⽤者隐藏核⼼开发细节

    十、__del__() ⽅法

    作用:设置对象被删除前的“临终遗言”,也可以检查当前某个对象是否被删除了。当对象被删除时会自动调用。

    创建对象后,python解释器默认调⽤ __init__() ⽅法;

    当删除⼀个对象时,python解释器也会默认调⽤⼀个⽅法,这个⽅法为 __del__() ⽅法
    class Dog:
        def __init__(self,name):
            self.name = name
        def __del__(self):
            print("%s 要被删除了"% self.name)
    dog1 = Dog("旺财")
    del dog1
    print("*****************")

    结果:

    旺财 要被删除了
    *****************

    当有1个变量保存了某个对象的引⽤时,此对象的引⽤计数就会加1,即此时dog1计数会加1.

    当使⽤del删除变量dog1时,只有当变量指向的对象的所有引⽤都被删除后,由于没有变量指向该对象,也就是引⽤计数为0时,才会真删除该对象(释放内存空间),这时才会触发 __del__() ⽅法

    class Dog:
        def __init__(self,name):
            self.name = name
        def __del__(self):
            print("%s 要被删除了"% self.name)
    dog1 = Dog("旺财")
    a = dog1
    del dog1
    print("*****************")

    虽然删除了dog1变量,但是仍然有a变量指向对象,所以先执行打印操作,等代码执行完,a变量才会被回收,所以对象才会被释放,从而触发__del__方法。

    class Dog:
        def __init__(self,name):
            self.name = name
        def __del__(self):
            print("%s 要被删除了"% self.name)
    def main():
        dog1 = Dog("旺财")
    main()
    print("*****************")

    由于函数内部定义的变量是局部变量,函数执行完成就被销毁了。

    在开发中,如果项目比较复杂时,可以用此方法来验证对象是否能正常销毁

     十一、继承

    ⾯向对象三⼤特性: 封装 继承 多态

    继承:某个类直接具备另⼀个类的能⼒(属性和⽅法)

    格式:

    class ⼦类名(⽗类名):

    作用:减少代码冗余,提升代码可读性。

    class Animal:
        def eat(self):
            print("-----吃-----")
        def drink(self):
            print("-----喝-----")
    class Dog(Animal):
        pass
    dog1 = Dog()
    dog1.eat()
    dog1.drink()

    结果:

    -----吃-----
    -----喝-----

     子类添加新功能

    class Animal:
        def eat(self):
            print("-----吃-----")
        def drink(self):
            print("-----喝-----")
    class Dog(Animal):
        def bark(self): print("-----汪汪叫------")
    dog1 = Dog()
    dog1.eat()
    dog1.drink()
    dog1.bark()

    多层继承

    class Animal:
        def eat(self):
            print("-----吃-----")
        def drink(self):
            print("-----喝-----")
    class Dog(Animal):
        def bark(self): print("-----汪汪叫------")
    class XTQ(Dog):
        """定义了⼀个哮天⽝ 类"""
        pass
    dog1 = XTQ()
    dog1.eat()
    dog1.drink()
    dog1.bark()

    结果:

    -----吃-----
    -----喝-----
    -----汪汪叫------

    注意:

    1)、⽗类中的 私有⽅法、私有属性,不会被⼦类继承

    2)、可以通过调⽤继承的⽗类的共有⽅法,间接的访问⽗类的私有⽅法、属性

    class Animal:
        def __init__(self):
            self.__num2 = 2
        def __run(self):
            print("-----跑-----")
        def eat(self):
            print("-----吃-----")
        def drink(self):
            print("-----喝-----")
    class Dog(Animal):
        def bark(self):
            print("-----汪汪叫------")
            self.__run()
         print(self.__num2) dog1
    = Dog() dog1.bark()

    结果:

    Traceback (most recent call last):
      File "F:workpythonProjectSecondDemoClassAndObject.py", line 99, in <module>
        dog1.bark()
      File "F:workpythonProjectSecondDemoClassAndObject.py", line 96, in bark
        self.__run()
    AttributeError: 'Dog' object has no attribute '_Dog__run'
    -----汪汪叫------

    私有方法也是一样的.

    class Animal:
        def __init__(self):
            self.__num2 = 2
        def __run(self):
            print("-----跑-----")
        def eat(self):
            print("-----吃-----")
        def drink(self):
            print("-----喝-----")
        def test(self):
            print(self.__num2)
            self.__run()
    class Dog(Animal):
        def bark(self):
            print("-----汪汪叫------")
            # self.__run()
            # print(self.__num2)
    dog1 = Dog()
    dog1.test()

    结果:

    2
    -----跑-----

    十二、重写⽗类⽅法

    当⼦类实现⼀个和⽗类同名的⽅法时,叫做 重写⽗类⽅法

    ⼦类重写了⽗类⽅法,⼦类再调⽤该⽅法将不会执⾏⽗类的处理。

    使用场景:当父类的某个方法不能满足我们的需求,重写父类方法,做自己需要的功能。

    class Animal:
        def eat(self):
            print("-----吃-----")
        def drink(self):
            print("-----喝-----")
    class Dog(Animal):
        def bark(self): print("-----汪汪叫------")
    class XTQ(Dog):
        def bark(self): print("----嗷嗷叫-----")
    dog1 = XTQ()
    dog1.bark()

    结果:

    ----嗷嗷叫-----

    调⽤被重写的⽗类⽅法

    ⼦类重写了⽗类⽅法,仍然想执⾏⽗类中的⽅法,两种方式:

    方式一:父类名.方法名(当前对象),注意点:需要手动传递对象

    class Animal:
        def eat(self):
            print("-----吃-----")
        def drink(self):
            print("-----喝-----")
    class Dog(Animal):
        def bark(self): print("-----汪汪叫------")
    class XTQ(Dog):
        def bark(self):
            Dog.bark(self)
    dog1 = XTQ()
    dog1.bark()

    方式二:super(类名,对象).方法名()

    class Animal:
        def eat(self):
            print("-----吃-----")
        def drink(self):
            print("-----喝-----")
    class Dog(Animal):
        def bark(self): print("-----汪汪叫------")
    class XTQ(Dog):
        def bark(self):
            super(XTQ,self).bark()
    dog1 = XTQ()
    dog1.bark()

    方式二的简写:super().方法名()

    class Animal:
        def eat(self):
            print("-----吃-----")
        def drink(self):
            print("-----喝-----")
    class Dog(Animal):
        def bark(self): print("-----汪汪叫------")
    class XTQ(Dog):
        def bark(self):
            super().bark()
    dog1 = XTQ()
    dog1.bark()

     十三、多继承

    所谓多继承,即⼦类有多个⽗类,并且具有它们的特征

    类名.__mro__查看类的继承链

    多继承中,多个父类有同名方法时,想要调用指定父类的方法,先重写此方法,再指定 父类名.方法(self)

    class A:
        def printA(self):
            print('----A----')
    # 定义⼀个⽗类
    class B:
        def printB(self):
            print('----B----')
    # 定义⼀个⼦类,继承⾃A、B
    class C(A,B):
        def printC(self):
            print('----C----')
    obj_C = C()
    obj_C.printA()
    obj_C.printB()

    结果:

    ----A----
    ----B----

    如果在上⾯的多继承例⼦中,如果⽗类A和⽗类B中,有⼀个同名的⽅法,那么通过⼦类去调⽤的时候,调⽤哪个?

    class A:
        def print(self):
            print('----A----')
    # 定义⼀个⽗类
    class B:
        def print(self):
            print('----B----')
    # 定义⼀个⼦类,继承⾃A、B
    class C(A,B):
        def print(self):
            # A.print(self)
            B.print(self)
    obj_C = C()
    print(C.__mro__)
    obj_C.print()

    结果:

    (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
    ----B----

     十四、多态

    多态的概念是应⽤于Java和C#这⼀类强类型语⾔中,⽽Python崇尚“鸭⼦类型”。

    所谓多态:定义时的类型和运⾏时的类型不⼀样,此时就成为多态。即需要使用父类对象的地方,也可以使用子类对象。

    Python伪代码实现Java或C#的多态

    Python “鸭⼦类型”:可以不是鸭子,长得像就行。并不强制数据的类型,只要数据完成了需要的处理,就可以执行,Python动态语言独有的特性。几乎可以理解为没有多态。

    鸭⼦类型语⾔中,函数/⽅法可以接受⼀个任意类型的对象作为参数/返回值,只要该对象实现了代码后续⽤到的属性和⽅法就不会报错

    鸭子类型是多态的升级版,

    class F1(object):
        def show(self):
            print('F1.show')
    class S1(F1):
        def show(self):
            print('S1.show')
    class S2(F1):
        def show(self):
            print('S2.show')
    def Func(obj):
        obj.show()
    s1_obj = S1()
    Func(s1_obj)
    s2_obj = S2()
    Func(s2_obj)

    结果

    S1.show
    S2.show

     十五、实例属性、类属性

    属性记录的数据不同时,定义实例属性。属性记录的数据始终相同时,定义类属性(节省内存)

    1、类属性定义格式:在类的内部,方法的外部,直接向定义变量一样。

    2、类属性也可以私有,私有后也只能在类的内部使用,前面加双下划线。

    3、修改类属性时,只能通过类对象来修改。

    4、实例对象不能修改类属性,而是创建了实例对象自己的属性

    5、类对象和实例对象都可以访问类属性

    注意:尽量不要类属性和实例属性同名,如果同名时,实例对象会访问自己实例属性,类对象访问类属性。如果有同名对象属性,实例对象会优先访问对象属性

    实例属性:

    通过类创建的对象 ⼜称为 实例对象,对象属性 ⼜称为 实例属性,记录对象各⾃的数据,不同对象的同名实例属性,记录的数据可能各不相同

    类属性:

    类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象 所共有。

    类属性可以使⽤ 类对象 或 实例对象 访问

    class Dog:
        type = "" # 类属性
    dog1 = Dog()
    dog1.name = "旺财"
    dog2 = Dog()
    dog2.name = "来福" # 类属性 取值
    print(Dog.type) # 结果:狗
    print(dog1.type) # 结果:狗
    print(dog2.type) # 结果:狗

    使⽤场景:

    类的实例 记录的某项数据 始终保持⼀致时,则定义类属性

    实例属性 要求 每个对象 为其 单独开辟⼀份内存空间 来记录数据,⽽ 类属性 为全类所共有 ,仅占⽤⼀份内存,更加节省内存空间。

     十六、类⽅法、静态⽅法

    类⽅法:

    类对象所拥有的⽅法

    需要⽤装饰器 @classmethod 来标识其为类⽅法,对于类⽅法,第⼀个参数必须是类对象,⼀般以 cls 作为第⼀个参数。

    class Dog(object):
        __type = ""
        # 类⽅法,⽤classmethod来进⾏修饰
        @classmethod
        def get_type(cls):
            return cls.__type
    print(Dog.get_type())

    使⽤场景:

    当⽅法中 需要使⽤类对象 (如访问私有类属性等)时,定义类⽅法

    类⽅法⼀般和类属性配合使⽤

    静态⽅法

    需要通过装饰器 @staticmethod 来进⾏修饰,静态⽅法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)。

    静态⽅法 也能够通过 实例对象 和 类对象 去访问。

    class Dog(object):
        type = ""
        def __init__(self):
            name = None
            # 静态⽅法
        @staticmethod
        def introduce(): # 静态⽅法不会⾃动传递实例对象和类对象
            print("⽝科哺乳动物,属于⻝⾁⽬..")
    dog1 = Dog() 
    Dog.introduce() # 可以⽤ 实例对象 来调⽤ 静态⽅法
    dog1.introduce() # 可以⽤ 类对象 来调⽤ 静态⽅法

    结果:

    ⽝科哺乳动物,属于⻝⾁⽬..
    ⽝科哺乳动物,属于⻝⾁⽬..

    使⽤场景:

    1、当⽅法中 既不需要使⽤实例对象(如实例对象,实例属性),也不需要使⽤类对象 (如类属性、类⽅法、创建实例等)时,定义静态⽅法

    2、取消不需要的参数传递,有利于 减少不必要的内存占⽤和性能消耗

    注意点:

    类中定义了同名的对象⽅法、类⽅法、静态⽅法时,调⽤⽅法会优先执⾏最后定义的⽅法

    class Dog:
        def demo_method(self):
            print("对象⽅法")
        @classmethod
        def demo_method(cls):
            print("类⽅法")
        @staticmethod
        def demo_method(): # 被最后定义,调⽤时优先执⾏
            print("静态⽅法")
    dog1 = Dog()
    Dog.demo_method() # 结果: 静态⽅法
    dog1.demo_method() # 结果: 静态⽅法

    结果

    静态⽅法
    静态⽅法
  • 相关阅读:
    Java安全之JNDI注入
    Visual Studio 2019 升级16.8之后(升级.Net 5),RazorTagHelper任务意外失败
    .Net Core 3.1升级 .Net 5后出现代码错误 rzc generate exited with code 1.
    重走py 之路 ——普通操作与函数(三)
    重走py 之路 ——字典和集合(二)
    设计模式结(完结篇)
    重走py 之路 ——列表(一)
    RestfulApi 学习笔记——分页和排序(五)
    RestfulApi 学习笔记——查询与过滤还有搜索(五)
    Android开发 Error:The number of method references in a .dex file cannot exceed 64K.Android开发 Error:The number of method references in a .dex file cannot exceed 64K
  • 原文地址:https://www.cnblogs.com/zwh0910/p/15255553.html
Copyright © 2011-2022 走看看