Python Class 详解

  • 1、基本概念
  • 1.1 类 `class`
  • 1.2 对象 `instance / object`
  • 1.3 面向对象三要素
  • 1.4 哲学思想
  • 2、类
  • 2.1 类的定义
  • 2.2 类对象及类属性
  • 2.3 类的实例化
  • 2.4 `__init__` 方法
  • 2.5 实例对象 `instance`
  • 2.6 示例 1
  • 2.7 示例 2
  • 2.8 示例 3
  • 3、对象属性
  • 3.1属性介绍
  • 3.2 实例属性的查找顺序
  • 3.3 示例 1
  • 3.4 示例 2
  • 3.5 示例 3
  • 4、类方法 和 静态方法
  • 4.1 类方法
  • 4.2 静态方法
  • 4.3 总结
  • 4.4 示例 1
  • 5、访问控制
  • 5.1 示例 1
  • 5.2 示例 2
  • 6、属性装饰器
  • 6.1 示例 1
  • 6.2 示例 2
  • 7、对象的销毁
  • 8、`__init__` 与 `__del__`
  • 8.1 示例 1
  • 8.2 示例 2
  • 9、`Class` 总结
  • 1、基本概念

    1.1 类 class

  • 类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合。
  • 用计算机的语言描述类,是属性和方法的集合。
  • 1.2 对象 instance / object

  • 对象是类的具象,是一个实体。
  • 对于我们每个人这个个体,都是抽象概念人类的不同的实体。
  • 对象属性:它是对象状态的抽象,用数据结构来描述
  • 对象操作:它是对象行为的抽象,用操作名和实现该操作的方法来描述
  • 1.3 面向对象三要素

  • 1 封装
    组装:将数据和操作组装到一起
    隐藏数据:对外只暴露一些接口,通过接口访问对象

  • 2 继承
    多复用,继承来的就不用自己写了
    多继承少修改,OCP(open-closed principle),使用继承来改变,来体现个性

  • 3 多态
    面向对象编程最灵活的地方,动态绑定

  • 1.4 哲学思想

  • 一切皆对象
  • 对象是数据和操作的封装
  • 对象是独立的,但是对象之间可以相互作用
  • 目前 OOP(Object-oriented programming) 是最接近人类认知的编程范式
  • 2、类

    2.1 类的定义

  • 1 必须使用 class 关键字
  • 2 类名必须是用 大驼峰 命名
  • 3 类定义完成后,就产生了一个类对象,绑定到了标识符 ClassName 上
  • class ClassName:
        语句块
    

    2.2 类对象及类属性

  • 1 类对象,类的定义执行后会生成一个类对象
  • 2 类的属性,类定义中的变量和类中定义的方法都是类的属性
  • 3 类变量,直接定义再类中的属性;一般来讲,类变量可使用全大写来命名
  • 2.3 类的实例化

  • 1 在类对象名称后面加上一个括号,就是调用类的实例化方法,完成实例化
  • 2 实例化就是真正的创建一个该类的对象(实例)
  • 3 每次实例化后获得的实例,都是不同的实例
  • 4 即使是使用同样的参数实例化,也得到不一样的对象
  • 5 Python 类实例化后,会自动调用 __init__ 方法,这个方法的第一个形式参数必须留给 self,其它参数随意
  • 2.4 __init__ 方法

  • 1 类的实例化,实际上就是调用的 __init__(self) 方法
  • 2 这个方法可以不定义,如果没有定义会在实例化后隐式调用
  • 3 作用:对实例进行初始化
  • 4 初始化参数可以多个参数,但是第一个位置必须是 self
  • 5 此方法不能有返回值,也就是只能 return None
  • 6 实例化调用__init__魔术方法来进行初始化-出厂配置,对生成的实例进行属性配置,__init__ 定义的是实例的属性
  • 2.5 实例对象 instance

  • 1 类实例化后一定会获得一个类的实例,就是 实例对象
  • 2 __init__ 方法的第一参数 self 就是指代某一个实例自身
  • 3 类实例化后,得到一个实例对象,调用类方法时,采用 instance.class_method() 的方式,实例对象就会绑定到方法上
  • 4 调用类方法时,类方法的 self 参数,就是 实例自身
  • 5 __init__ 中所定义的变量,是保存在 实例 上的,并不是 类 上,所以,称为 实例变量
  • 6 实例变量是每一个实例自己的变量,是自己独有的
  • 5 类变量是类的变量,是类的所有实例共享的属性和方法
  • 2.6 示例 1

    class MyClass:
        """My first class"""
        print('in class')
        xx = 'abc'   # 类的属性
        
        def __init__(self, x, y):
            print('in init')
            self.x = x
            self.y = y
        
        def foo(self):
            print('in foo')
            return "My class"
        
    print(MyClass.xx, MyClass.foo, MyClass.__doc__)
    myc1 = MyClass(1, 2)
    myc1 = MyClass(1, 2)
    print(myc1.foo())
    print(MyClass.foo(myc1))
    
    # 上例中 xx 和 foo 都是类的属性,__doc__ 也是类的特殊属性
    # foo 是 方法method,本质上就是普通函数对象 function
    # 第一个形式参数 self,指代当前实例本身
    
    in class
    abc <function MyClass.foo at 0x000002A221E76F70> My first class
    in init
    in init
    in foo
    My class
    in foo
    My class
    

    2.7 示例 2

    # self
    class MyClass:
        def __init__(self):
            print(1, 'self in init = {}'.format(id(self)))
            
        def showself(self):
            print(2, 'self in showself() = {}'.format(id(self)))
    
    c = MyClass()
    print('After instance c')
    print(3, 'c = {}'.format(id(c)))
    print('=' * 30)
    c.showself()
    print(MyClass.showself)
    print(c.showself)
    
    # 类实例化后,得到一个实例对象,调用类方法时,采用 instance.class_method() 的方式,实例对象就会绑定到方法
    # self 就是调用者,self 这个名字只是一个惯例,可以修改,但最好不要修改
    # 查看打印结果,思考一下代码的执行顺序
    
    1 self in init = 2895381183888
    After instance c
    3 c = 2895381183888
    ==============================
    2 self in showself() = 2895381183888
    <function MyClass.showself at 0x000002A221D851F0>
    <bound method MyClass.showself of <__main__.MyClass object at 0x000002A2222ABD90>>
    

    2.8 示例 3

    # 实例变量和类变量
    # 实例变量是每一个实例自己的变量,是自己独有的
    # 类变量是类的变量,是类的所有实例共享的属性和方法
    
    class Person:
        age = 3
        def __init__(self, name):
            self.name = name
    
    tom  = Person('Tom')
    jerry = Person('Jerry')
    print(tom.name, jerry.name)
    print(tom.age, jerry.age)
    print(Person.age)
    Person.age = 30
    print(Person.age,  tom.age, jerry.age)
    
    Tom Jerry
    3 3
    3
    30 30 30
    

    3、对象属性

    3.1属性介绍

  • 1 特殊属性 __name__ __class__ __dict__ __qualname__

  • 2 Python 中每一种对象都拥有不同的属性

  • 3 函数、类都是对象,类的实例也是对象

  • 4 类属性保存在类的__dict__中,实例属性保存在实例的__dict__

  • 5 属性是类的,也是这个类所有实例的,其它实例都可以访问到

  • 6 属性是实例的,就是这个实例自己的,通过类访问不到

  • 7 对象(实例或类)可以动态的给自己增加一个属性(赋值即定义一个新属性)

  • 8 实例.__dict__[变量名]实例.变量名 都可以访问到实例自己的属性(注意这两种访问是有本质区别的)

  • 9 实例的同名变量会隐藏掉类变量,或者说是覆盖了这个类变量,但是注意类变量还在那里,并没有真正的被覆盖

  • 10 类方法(也是类属性)在定义时,第一个参数必须时 self,而 self 必须指向一个对象,也就是类实例化之后,由实例来调用这个方法

    class Person:
        pass
    
    Person.__name__  # 'Person'
    Person.__class__.__name__, type(Person).__name__  # ('type', 'type')
    # 结果是字符串
    type(Person), Person.__class__  # (type, type)
    # 结果是类
    
  • 3.2 实例属性的查找顺序

  • 1 实例使用 .点号 来访问属性,会先找自己的 __dict__,如果没有,再通过属性 __class__ 找到自己的类,再去类的 __dict__ 中找
  • 2 如果实例使用 __dict__[变量名] 来访问变量,就不会按照上面的查找顺序找变量了,这是指明使用字典的 key 查找,不是属性查找
  • 3.3 示例 1

    class Person:
        age = 3
        def __init__(self, name):
            self.name = name
            
    print('----- class -----')
    print(Person.__class__, type(Person))
    print(sorted(Person.__dict__.items()))
    print('=' * 66)
    tom = Person('Tom')
    print(tom.__class__, type(tom))
    print(sorted(tom.__dict__.items()))
    print('=' * 66)
    print(tom.__class__.__name__)
    print(sorted(tom.__class__.__dict__.items()))
    print(sorted(type(tom).__dict__.items()))
    
    ----- class -----
    <class 'type'> <class 'type'>
    [('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x000002A2221BB790>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]
    ==================================================================
    <class '__main__.Person'> <class '__main__.Person'>
    [('name', 'Tom')]
    ==================================================================
    Person
    [('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x000002A2221BB790>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]
    [('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x000002A2221BB790>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 3)]
    

    3.4 示例 2

    class Person:
        age = 3
        height = 170
        
        def __init__(self, name, age=18):
            self.name = name
            self.age = age
        
    tom = Person('Tom')
    jerry = Person('Jerry', 20)
    
    Person.age = 30
    print(Person.age, tom.age, jerry.age)  # 30 18 20
    print(Person.height, tom.height, jerry.height)  # 170 170 170
    print(jerry.__dict__)  # {'name': 'Jerry', 'age': 20}
    jerry.height = 175  
    print(Person.height, tom.height, jerry.height)  # 170 170 175
    print(jerry.__dict__)  # {'name': 'Jerry', 'age': 20, 'height': 175}
    tom.height += 10
    print(Person.height, tom.height, jerry.height)  # 170 180 175
    Person.height += 15
    print(Person.height, tom.height, jerry.height)  # 185 180 175
    Person.weight = 70
    print(Person.weight, tom.weight, jerry.weight)  # 70 70 70
    print(tom.__dict__) # {'name': 'Tom', 'age': 18, 'height': 180}
    print(tom.__class__.__dict__['weight'])  # 70
    print(tom.weight)  # 70
    # print(tom.__dict__['weight'])  # KeyError: 'weight'
    

    3.5 示例 3

    class MyClass:
        def normal_method():
            print('Normal Method')
    
    MyClass.normal_method()  # Normal Method
    # MyClass().normal_method()  # normal_method() takes 0 positional arguments but 1 was given
    # 由于 normal_method 在定义的时候没有指定 self,所以不能完成实例对象的绑定
    # 不能用 MyClass().normal_method() 调用
    # 严禁这么使用,做实验只是为了加深大家对 类 的理解
    

    4、类方法 和 静态方法

    4.1 类方法

  • 1 在定义中,使用 @classmethod 装饰器修饰的方法
  • 2 必须至少有一个参数,且第一个参数留给了 clscls 指代调用者即类对象自身
  • 3 cls 这个标识符可以是任意合法名称,但是为了易读,请不要修改
  • 4 通过 cls 可以直接操作类的属性
  • 5 无法通过 cls 操作类的实例
  • 4.2 静态方法

  • 1 在类定义中,使用 @staticmethod 装饰器修饰的方法
  • 2 调用时,不会隐式的传入参数
  • 3 静态方法,只是表明这个方法属于这个名词空间。函数归在一起,方便组织管理
  • 4.3 总结

  • 1 类几乎可以调用所有内部定义的方法,但是调用普通的方法 时会报错,原因是第一参数必须是类的实例
  • 2 实例几乎也可以调用所有的方法,普通的函数的调用一般不可能出现,因为不允许这么定义
  • 3 类除了普通方法都可以调用,普通方法需要对象的实例作为第一参数
  • 4 实例可以调用所有类中定义的方法(包括类方法/静态方法),普通方法传入实例自身,静态方法和类方法需要找到实例的类
  • 4.4 示例 1

    class Person:
        def method(self):
            print("{}'s method".format(self))
        
        @classmethod
        def class_method(cls):
            print('class = {0.__name__}({0})'.format(cls))
            cls.HEIGHT = 170
        
        @staticmethod
        def static_method():
            print(Person.HEIGHT)
        
    # 类访问
    # print(Person.method()) # method() missing 1 required positional argument: 'self'
    print(Person.class_method())  # class = Person(<class '__main__.Person'>)  None
    print(Person.static_method())  # 170 None
    print(Person.__dict__)
    # {'__module__': '__main__', 'method': <function Person.method at 0x000002A2220C34C0>, 'class_method': <classmethod object at 0x000002A22294BB80>, 'static_method': <staticmethod object at 0x000002A22294B340>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, 'HEIGHT': 170}
    
    tom = Person()
    print(tom.method())  # <__main__.Person object at 0x000002A222314970>'s method  None
    print(tom.class_method())  # class = Person(<class '__main__.Person'>)  None
    print(tom.static_method())  # 170  None
    

    5、访问控制

  • 私有属性:使用双下划线开头的属性名;类定义的时候,如果声明一个实例变量的时候,使用双下划线,Python解释器会将其改名,转换名称为 _类名__变量名的名称。【注意,隐藏属性改名的只和当前类有关系,设置私有属性的当前类,在谁里面改谁的名】
  • 保护属性:在变量名前使用一个下划线,保护属性没有改变名称,和普通属性一样,解释器不做任何特殊处理。看见这种变量,就如同私有变量,不要直接使用
  • 私有方法:单下划线的方法只是开发者之间的约定,解释器不做任何改变。双下划线会改变名称,同属性一样
  • Python中使用单 双下划线来标识一个成员被保护或者被私有化隐藏起来,不要万不得已,不要修改
  • 5.1 示例 1

    class Person:
        def __init__(self, name, age=10):
            self.name = name
            self.__age = age
        
        def growup(self, i=1):
            if i > 0 and i < 100:
                self.__age += i
        
        def showage(self):
            return self.__age
            
    p1 = Person('Tom')
    print(p1.__dict__)  # {'name': 'Tom', '_Person__age': 10}
    p1.growup(55)
    print(p1.__dict__)  # {'name': 'Tom', '_Person__age': 65}
    # print(p1.__age)  #  'Person' object has no attribute '__age'
    print(p1._Person__age)  # 65
    print(p1.showage())  # 65  这是正确访问隐藏属性的方法
    p1.__age = 100
    print(p1.showage())  # 65
    print(p1.__age)  # 100
    print(p1.__dict__)  # {'name': 'Tom', '_Person__age': 65, '__age': 100}
    

    5.2 示例 2

    class Person:
        def __init__(self, name, age=18):
            self.name = name
            self._age = age
    
    tom = Person('Tom')
    # print(tom.age)  # 'Person' object has no attribute 'age'
    print(tom.__dict__)  # {'name': 'Tom', '_age': 18}
    print(tom._age)  # 18
    

    6、属性装饰器

  • 属性装饰器:使用property装饰器的时候,方法要同名
  • 使用了属性装饰器时,函数方法就会变为属性,可以直接使用. + 属性名称进行访问
  • 1 property装饰器:后面跟的函数名就是以后的属性名,它就是getter,这个必须有,有了它至少是只读属性
  • 2 setter装饰器:与属性名同名,且接收两个参数,第一个是self,第二个是将要赋值的值,有了它,属性可写
  • 3 deleter装饰器:可以控制是否删除属性,很少用
  • 4 property装饰器必须在前,setter deleter装饰器在后
  • 5 property装饰器能通过简单的方式,把对方法的操作变成对属性的访问,并起到了一定隐藏结果
  • class A:
        def test(self):
            print('test 1')
     
    A().test()
    # test 1
    
    class A:
        @property
        def test(self):
            print('test 1')
     
    # A().test()  # 'NoneType' object is not callable
    A().test
    # test 1
    

    6.1 示例 1

    class Person:
        def __init__(self, name, age=18):
            self.name = name
            self.__age = age
            
        @property
        def age(self):
            print('getter')
            return self.__age
        
        @age.setter
        def age(self, age):
            print('setter')
            self.__age = age
        
        @age.deleter
        def age(self):
            print('deleter')
            del self.__age
            
    tom = Person('Tom')
    print(tom.age)  # getter  18
    tom.age = 22  # setter
    print(tom.age)  # getter  18
    print(tom.__dict__)  # {'name': 'Tom', '_Person__age': 22}
    del tom.age  # deleter
    print(tom.__dict__)  # {'name': 'Tom'}
    

    6.2 示例 2

    class Person:
        def __init__(self, name, age=18):
            self.name = name
            self.__age = age
            
        def getage(self):
            print('getter')
            return self.__age
        
        def setage(self, age):
            print('setter')
            self.__age = age
        
        def delage(self):
            print('deleter')
            del self.__age
        
        age = property(getage, setage, delage, 'age property')
        
    tom = Person('Tom')
    print(tom.age)  # getter  18
    tom.age = 22  # setter
    print(tom.age)  # getter  18
    print(tom.__dict__)  # {'name': 'Tom', '_Person__age': 22}
    del tom.age  # deleter
    print(tom.__dict__)  # {'name': 'Tom'}
    

    7、对象的销毁

  • 类中可以定义 __del__ 方法,称为析构函数(方法)。
  • 作用:销毁类的实例的时候调用,以释放占用的资源。其中就放些清理资源的代码,比如释放链接。
  • 注意这个方法不能引起对象的真正的销毁,只是对象销毁的时候会自动调用它。
  • 使用del语句删除实例,引用计数减1,当引用计数为0时,会自动调用 __del__ 方法。
  • 由于Python实现了垃圾回收机制,不能确定对象何时执行垃圾回收。
  • 由于垃圾回收对象销毁时,才会真正清理对象,还会再回收对象之前自动调用 __del__ 方法,除非你明确知道自己的目的,否则不要手动调用这个方法
  • import time
    
    class Person:
        def __init__(self, name, age=18):
            self.name = name
            self.__age = age
            
        def __del__(self):
            print('delete {}'.format(self.name))
    
    
    tom = Person('tom')
    tom.__del__()
    tom.__del__()
    tom.name
    tom2 = tom
    tom3 = tom2
    print(tom, tom2, tom3)
    del tom
    time.sleep(3)
    del tom2
    print('=' * 33)
    del tom3
    
    delete tom
    delete tom
    <__main__.Person object at 0x000002A221A59F10> <__main__.Person object at 0x000002A221A59F10> <__main__.Person object at 0x000002A221A59F10>
    =================================
    delete tom
    

    8、__init____del__

  • 构造方法__init__,具有初始化的作用,也就是当该类被实例化的时候就会自动执行该函数。那么通常就可以把要先初始化的属性放到这个方法里面
  • 析构方法__del__是对象在被垃圾回收的时候起作用的一个方法,它的执行一般也就意味着对象不能够继续引用, 回收内存
  • 8.1 示例 1

    运行脚本结束后,系统会自动执行析构函数,所以 ‘del method’ 晚于 'The last line code in the test5.py' 打印。

    class Person:
    
        def __init__(self):
            print('init method')
    
        def __del__(self):
            print('del method')
    
    p = Person()
    print('The last line code in the test5.py')
    
    D:\PycharmProjects\pythonProject\202108089\venv\Scripts\python.exe D:/PycharmProjects/pythonProject/202108089/test5.py
    init method
    The last line code in the test5.py
    del method
    
    Process finished with exit code 0
    

    8.2 示例 2

    del 语句执行时,内存立即被回收,所以 ‘del method’ 早于 'The last line code in the test6.py' 打印

    class Person:
    
        def __init__(self):
            print('init method')
    
        def __del__(self):
            print('del method')
    
    p = Person()
    del p
    print('The last line code in the test6.py')
    
    D:\PycharmProjects\pythonProject\202108089\venv\Scripts\python.exe D:/PycharmProjects/pythonProject/202108089/test6.py
    init method
    del method
    The last line code in the test6.py
    
    Process finished with exit code 0
    

    9、Class 总结

  • 类是抽象的,对象是具体的,类就是属性和方法的集合。属性 一般使用数据结构来保存,方法 操作或者动作或者能力

  • 类对象是由type构造出来的对象,其实类对象也是一个实例,由type创造出来的实例(涉及到元编程,后续在更新)

    class MyClass:
        pass
    
    A = MyClass()
    
    type(MyClass), type(A)  # (type, __main__.MyClass)
    type(int), type(str)  # (type, type)  
    # int str 都是类对象,自定义类请全使用大驼峰命名方式
    
  • 类属性和类变量 可以使用 . 来进行获取,每次实例化都会产生不同的对象,单例模式除外

  • 类方法:不管是实例调用,还是类调用,都会自动把类注入为第一参数;换言之,第一个参数永远是cls,如果写为别的参数名称或者不写参数时,实际运行时,也会把类作为第一个参数

  • 静态方法:可以不需要任何参数,类和实例都可以调用;如果有类似需求,可以使用静态方法(静态方法装饰器会阻止任何参数注入

  • 普通的类方法(带self,也就是有参):如果有参数的话,第一个参数肯定是self,如果写为别的参数名称,实际运行时,如果是实例,会注入实例本身self作为参数;如果是类调用,则是任何参数就行;因为实例有绑定效果,类没有绑定;普通的类方法(不带self,也就是无参):语法可以,但是禁止这么使用,如果有无参方法需求的话,请使用静态方法

  • 为了大家便于查阅代码,self cls 等参数,大家不要随意替换。

  • class MyClass:
        
        def showmyclass(self):
            print(id(self))
    
    A = MyClass()
    B = MyClass()
    
    MyClass.showmyclass
    # <function __main__.MyClass.showmyclass(self)>
    A.showmyclass  # 实例对象和self绑定在一起,在调用时,是不需要输入self参数
    # <bound method MyClass.showmyclass of <__main__.MyClass object at 0x0000017DB2795DF0>>
    
    # MyClass.showmyclass()  # showmyclass() missing 1 required positional argument: 'self'
    A.showmyclass()  # 1639376838128
    MyClass.showmyclass(A)  # 1639376838128
    

    来源:Lee木木

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python Class 详解

    发表评论