• (python)面向对象


    一、面向对象概述

      要了解面向对象,就需要先了解面向过程的概念,那么什么是面向过程编程呢?最具代表性的就是C语言了,所谓面向过程编程就是在做一件事的时候,需要按步骤进行,第一步干什么,第二步干什么,这种编程方式适合问题规模较小,需要步骤化处理逻辑的业务。

      了解了面向过程编程,那么就容易理解面向对象编程了,官方解释是一种认识世界、分析世界的方法论,将万事万物都抽象为类的方法。这种概念较为抽象不方便理解,以生活中的示例为例,所谓面向对象就是你吃鱼,你属于一个对象(实例),鱼也属于一个对象(实例),那么吃就是动作(方法)了。我们可以将具体的事物抽象成对象(实例)(人、鱼是有无数具体的个体抽象出来的),将动作抽象成方法,这种抽象方式就是面向对象编程,而python使用的正是这种编程方式。

      之前面向对象有两个概念,类和对象。那么什么是类呢,类是一种抽象的概念,是万事万物的抽象,是一类事物共同特征的集合,也就是属性和方法的集合。现在又出来了属性和方法,所谓属性就是对象状态的抽象,用数据结构来描述,通俗来讲每个人都有名字、身高体重等信息,这些信息是个人的属性,但是,这些信息不属于特定的某一个人,它是抽象的概念,不能保留具体的值;所谓方法就是对象行为的抽象,用操作名和实现该操作的方法来描述,通俗来讲就是类中解决某一实现方法的功能。什么是对象呢?对象就是抽象的类的具体的实例,一个类可以有多个对象(实例),每个对象都有其特有的属性,可以通过具体的实例来实现自己需要实现的方法。

    二、面向对象的三要素

    1、封装

      将数据和操作组装到一起,对外只暴露一些接口,通过接口去访问对象。通俗来讲就是你驾驶一辆汽车,不需要去了解汽车的构造细节,只需要知道使用什么部件怎么驾驶就可以了。

    2、继承

      可以继承父类的某些方法和属性,也可以重写父类的某些方法和属性实现个性化,多复用性。通俗来讲就是人类继承自动物类,孩子继承父母的特征。有单一继承和多继承。

    3、多态

      继承自动物类的人类,人类的“吃”操作和猫类的不同。这是面向对象编程最灵活的地方。

    三、类的抽象

    一、类的定义

    1、语法:

    1 class ClassName:
    2     语句块
    • 使用class关键字来定义一个类
    • 类名必须使用大驼峰的方式来进行命名(每个单词的首字母大写)
    • 类在定义完成后,就产生了一个类对象,绑定到了标识符ClassName上

    2、举例

     1 class MyClass:
     2     """A example class"""
     3     x = "abc"  # 类属性,类变量
     4 
     5     def foo(self):  # 类属性,类方法
     6         return "My Class"
     7 
     8 
     9 print(MyClass.x)  # abc
    10 print(MyClass.foo)  # <function MyClass.foo at 0x000000E545931840>
    11 print(MyClass.__doc__)  # A example class
    • 类对象:类的定义就会生成一个类对象。
    • 类属性:类中的变量和方法都是类的属性。
    • 可以通过类名直接调用其中的属性,x、foo都是类的属性,__doc__也是属性。
    • foo是方法对象method,不是普通的函数对象function了,它一般要求至少有一个参数,第一个参数就是self(标识符,可以换其他名字,但是不建议换,规范),这个参数位置就留给了self,self指代当前实例本身。

    四、实例化

      抽象出来的类,可以通过具体的实例来实现特有的属性和方法,下面来演示实现类的实例化。

    a = ClassName()  # 实例化

      使用上面的语法,就调用了类的实例化方法,完成实例化,创建了一个该类的对象(实例),例如:

    tom = Person()
    jerry = Person()

      上面的tom、jerry都是类Person的实例,它们两个是不同的实例,即使使用同样的参数实例化,得到的也是不同的对象。

      在实例化之后,会自动调用__init__方法进行初始化。

    1、__init__方法

      对实例进行初始化,MyClass()实际上调用的就是__init__(self)方法,可以不去定义,它会隐式去调用这个方法。演示如下:

    class MyClass:
        def __init__(self):
            print("实例化之后调用init方法")
    
    MyClass()  # 实例化之后调用init方法

      init可以传递多个参数,注意第一个位置必须是self,并且不能有返回值。可以通过实例化后的对象去调用类中的属性:

    class Person:
        def __init__(self, name, age):
            self.name = name  # 将实例化传递进来的参数赋值给类属性
            self.age = age
    
        def showage(self):
            print("{} is {}".format(self.name, self.age))  # 通过self可以获取该类的属性
    
    
    tom = Person("tom", 22)  # 实例化
    jerry = Person("jerry", 23)
    
    # 通过实例化后的对象可以调用其属性
    print(tom.name, jerry.name)  # tom jerry
    jerry.age += 1
    print(jerry.age)  # 24
    jerry.showage()  # jerry is 24

    2、实例变量和类变量

      实例变量是每一个实例自己独有的,通过类名是访问不到的;类变量是所有实例共享的属性和方法,其实例都可以访问:

    class Person:
        age = 3  # 类变量
    
        def __init__(self, name):
            self.name = name  # 实例变量
    
    tom = Person("tom")
    jerry = Person("jerry")
    
    print(tom.name, tom.age)  # tom 3
    print(jerry.name, jerry.age)  # jerry 3
    Person.age = 30
    print(Person.age, tom.age, jerry.age)  # 30 30 30

    3、对象的特殊属性

      每一个对象都拥有不同的属性,函数、类都是对象,类的实例也是对象。

    特殊属性 含义
    __name__ 对象名
    __class__ 对象的类型
    __dict__ 对象的属性的字典
    __qualname__ 类的限定名

      举例如下:

    class Person:
        age = 3  # 类变量
    
        def __init__(self, name):
            self.name = name  # 实例变量
    
    # 类的属性
    print(Person.__class__.__name__)  # type
    print(sorted(Person.__dict__.items()))  # [('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x0000008C5F541840>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]
    
    # 实例的属性
    tom = Person("tom")
    print(tom.__class__.__name__)  # Person
    print(sorted(tom.__dict__.items()))  # [('name', 'tom')]
    
    # 通过实例访问类的属性
    print(tom.__class__.__name__)  # Person
    print(sorted(tom.__class__.__dict__.items()))  # [('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x0000008C5F541840>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]

      实例属性的查找顺序:实例会先找自己的__dict__,如果没有然后通过属性__class__找到自己的类,再去类的__dict__中去找。

    五、类装饰器

      本质上是为类对象动态增加了一个属性,而Person这个标识符指向这个类对象。

    def add_name(name):
        def wrapper(cls):
            cls.name = name
            return cls
    
        return wrapper
    
    @add_name("tom")
    class Person:
        age = 3
    
    print(Person.name)  # tom

    六、类方法和静态方法

      静态方法,不需要实例。静态方法主要是用来存放逻辑性的代码,主要是一些逻辑属于类,但是和类本身没有交互,即在静态方法中,不会涉及到类中的方法和属性的操作。可以理解为将静态方法存在此类的名称空间中。

      类方法是将类本身作为对象进行操作的方法。他和静态方法的区别在于:不管这个方式是从实例调用还是从类调用,它都用第一个参数把类传递过来

    class Person:
        AGE = 20
    
        @classmethod
        def class_method(cls):
            print("class method")
            cls.AGE = 170
    
        @staticmethod
        def static_method():
            print("static method")
    
    Person.class_method()  # class method
    Person.static_method()  # static method
    print(Person.__dict__)  # {'__doc__': None, 'static_method': <staticmethod object at 0x0000008CC02BAEF0>, 'AGE': 170, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, 'class_method': <classmethod object at 0x0000008CC029D470>}

    七、访问控制

    1、私有变量

      使用双下划线开头的属性名,就是私有属性。将属性设为私有,让外部访问不到,其本质就是将该属性名改名,改为(_类名__变量名),使用原来的属性名就访问不到了。那么如何去访问这个私有属性呢,可以通过方法来访问该私有属性。

    class Person:
        def __init__(self, name, age=18):
            self.name = name
            self.__age = age
    
        def growup(self, i=1):
            if i>0 and i<100:
                self.__age += 1
    
        # 私有属性通过方法去访问
        def getage(self):
            return self.__age
    
    tom = Person("tom")
    tom.growup(20)
    # print(tom.__age)  # AttributeError: 'Person' object has no attribute '__age'
    print(tom.__dict__)  # {'name': 'tom', '_Person__age': 19}
    print(tom.getage())  # 19

    2、私有方法

      私有方法的本质和私有变量的本质一样,只是将方法的名称改变(_类名__方法名)

    class Person:
        def __init__(self, name, age=18):
            self.name = name
            self.__age = age
    
        # 保护方法,约定,没有改名
        def _getage(self):
            return self.__age
    
        # 私有方法,改名
        def __getage(self):
            return self.__age
    
    tom = Person("tom")
    print(tom._getage())  # 18
    # print(tom.__getage())  # AttributeError: 'Person' object has no attribute '__getage'
    print(tom.__class__.__dict__)  # {'__dict__': <attribute '__dict__' of 'Person' objects>, 'growup': <function Person.growup at 0x0000000935BC1A60>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, '__init__': <function Person.__init__ at 0x0000000935BC1840>, '_Person__getage': <function Person.__getage at 0x0000000935BC1AE8>, '__module__': '__main__'}
    print(tom._Person__getage())  # 18

    3、保护变量、方法

      在变量或者方法名前面加上单下划线,就是保护变量、方法。这种变量或者方法可以直接访问,并没有改名,这只是开发者共同的约定,看到这种变量就如同私有变量,不要直接使用。

    class Person:
        def __init__(self, name, age=18):
            self.name = name
            self._age = age  # 保护变量
    
        # 保护方法,约定,没有改名
        def _getage(self):
            return self._age
    
    tom = Person("tom")
    print(tom._age)  # 18
    print(tom._getage())  # 18
    print(tom.__dict__)  # {'_age': 18, 'name': 'tom'}
    print(Person.__dict__)  # {'__module__': '__main__', '__doc__': None, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__init__': <function Person.__init__ at 0x000000E3DB071840>, '_getage': <function Person._getage at 0x000000E3DB071A60>}

     八、补丁

      在运行时,对属性、方法、函数等进行动态替换。补丁往往是为了通过替换,修改来增强、扩展原有代码的能力。

    # t1(原代码)
    class Person:
        def get_score(self):
            ret = {"English": 78, "Chinese": 100, "History": 89}
            return ret
    # t2(打补丁)
    from t_class.t1 import Person
    
    # 要修改的内容
    def get_score(self):
        return dict(name=self.__class__.__name__, English=88, Chinese=38, History=100)
    
    # 打补丁
    def monkeypatchPerson():
        Person.get_score = get_score
    
    if __name__ == '__main__':
        monkeypatchPerson()
        print(Person().get_score())

    九、属性装饰器

      一般好的设计是将实例的属性保护起来,不让外部去直接访问,外部使用getter和setter方法去获取和设置属性。

    class Person:
        def __init__(self, name, age):
            self.name = name
            self.__age = age
    
        @property
        def age(self):
            return self.__age
    
        @age.setter
        def age(self, age):
            self.__age = age
    
        @age.deleter
        def age(self):
            del self.__age
            print("del age")
    
    tom = Person("tom", 22)
    print(tom.age)  # 22
    tom.age = 18
    print(tom.age)  # 18
    del tom.age  # del age
    • 使用property装饰器的时候这三个方法同名
    • property装饰器:它就是getter,这个必须有,有了它至少是只读属性
    • setter装饰器:接收两个参数,第一个是self,第二个是要修改的值,有了它属性可写
    • deleter装饰器:删除属性,很少用
  • 相关阅读:
    Redis
    cz_health_day13项目实战
    cz_health_day11
    cz_health_day10
    cz_health_day09
    cz_health_day08
    MySQL8管理系列之二:从5.5升级到8的问题处理
    MySQL8管理系列之一:Mysql 8.0以后版本的安装
    MySQL 5.5.x 数据库导入到 8.0.x 服务器
    修改Mysql 8.0版本的默认数据库目录
  • 原文地址:https://www.cnblogs.com/Sweltering/p/9945456.html
Copyright © 2020-2023  润新知