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() # 结果: 静态⽅法

    结果

    静态⽅法
    静态⽅法
  • 相关阅读:
    .Net时间计算函数,统计某一天是一年的第几周,这一周从哪天开始到哪天结束
    1分钟搞定超慢SQL
    网站
    舞台
    相见欢
    一套完整系统对人生的意义
    2015/08/15心情
    Linux下压缩某个文件夹(文件夹打包)
    init进程 && 解析Android启动脚本init.rc && 修改它使不启动android && init.rc中启动一个sh文件
    andriod系统裁剪心得
  • 原文地址:https://www.cnblogs.com/zwh0910/p/15255553.html
Copyright © 2011-2022 走看看