Python系列(8)- Python 类和对象、模块和包

 

1. 类和对象

    Python 语言在设计之初,就定位为一门面向对象的编程语言,"Python 中一切皆对象" 就是对 Python 这门编程语言的完美诠释。

    类和对象是 Python 的重要特征,相比其它面向对象语言,Python 很容易就可以创建出一个类和对象。同时,Python 也支持面向对象的三大特征:封装、继承和多态。

    1) 面向对象

        面向对象编程(Object-oriented Programming,简称 OOP),是一种代码封装的方法。
        
        函数封装是一种将程序段或功能打包成可重用的代码块的过程。函数是一种面向过程的编程方法,它将一段可以执行特定任务的代码封装起来,形成一个独立的单元。

        类封装是面向对象编程的核心概念之一。类是一种用户定义的应用数据类型,它包含了属性(变量)和方法(函数)。类封装将数据和操作封装在一起,形成一个有机的整体。类提供了更高的抽象层次,可以定义对象的行为和状态。
        
        类封装更适合于复杂系统的开发,而函数封装则适用于简单的功能实现。

        面向对象相关术语:

            (1) 类(Class): 用来描述具有相同的属性 (变量)和方法(函数)的对象的集合;
            (2) 对象:对象是类的实例化,类是一个模板,对象是根据这个模板创建的具体实例;
            (3) 类属性(或称类变量): 在类体中方法(函数)之外的变量,它是类的所有实例化对象共享的变量;
            (4) 实例属性(或称实例变量):在类体中方法(函数)之内,以 "self.变量名=值" 的方式定义的变量;
            (5) 局部变量:在类体中方法(函数)之内,以 "变量名=值" 的方式定义的变量;
            (6) 方法:;
            (7) 继承:即一个派生类(derived class)继承基类(base class)的属性和方法。继承也允许把一个派生类的对象作为一个基类对象对待;
            (8) 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写;
            (9) 多态:;

            注:属性‌和变量是两个不同的概念,变量是面向过程编程、面向对象编程中都会用到的概念,属性则是面向对象编程中的一个概念,通常在类中一个属性对应一个变量,并通过 getter 和 setter 方法进行读写。即在面向对象编程时,定义/读写属性,需要比定义/读写变量采用更严谨的规范要求。
                方法和函数也是两个不同的概念,函数是面向过程编程、面向对象编程中都会用到的概念,方法则是面向对象编程中的一个概念,通常在类中一个方法对应一个函数。

    2) 类的定义和实例化

        Python 定义类使用 class 关键字,语法格式如下:

            class 类名:
                [类属性 = 值]

                def __init__(self, [参数列表]):
                    [self.实例属性 = 参数值]

                def 方法(self, [参数列表]):
                    [self.实例属性 = 参数值]
                    [局部变量 = 参数值]
                
            名词解释:

                (1) 类名:一个符合 Python 语法的标识符,类名要能够体现出该类的特征;
                (2) [类属性 = 值]: 是可选项,表示定义一个类属性,并赋值;
                (3) __init__(self, [参数列表]): __init__ (双下划线开头/结尾)是类的构造方法,创建类的对象时,会自动调用这个方法;self 表示类实例化的对象本身;[参数列表] 是可选项,和函数参数列表一样,多个参数之间用逗号分隔;
                (4) [self.实例属性 = 参数值]:是可选项,表示定义一个实例属性,并赋值;
                (5) 方法(self, [参数列表]): 方法是类的成员函数;self 表示类实例化的对象本身;[参数列表] 是可选项,和函数参数列表一样,多个参数之间用逗号分隔;

                注:[] 括表示非必选项。

        类的实例化(即创建对象),语法格式如下:

            变量 = 类名([参数列表])

            名词解释:

                (1) 类名: 就是定义类时的类名;
                (2) [参数列表]: 和函数参数列表一样,多个参数之间用逗号分隔;
                (3) 变量: 类实例化的对象。
        
        示例:

            #!/usr/bin/python3
            # -*- coding: UTF-8 -*-

            class Dog:
                def display(self):
                    print('This is a dog')

            class Person:
                def __init__(self, name):
                    self.name = name

                def display(self):
                    print('name:', self.name)

            if __name__ == "__main__":

                dog = Dog()
                dog.display()

                person = Person('Python')
                person.display()

           
        输出结果如下:

            This is a dog
            name: Python

        注:Dog 类没有 __init__() 构造方法,Python 也会自动给 Dog 类添加一个仅包含 self 参数的构造方法,这种构造方法被称为类的默认构造方法。

    3) 类中的属性

        根据变量定义的位置不同,以及定义的方式不同,类中的属性可细分为:类属性、实例属性、局部变量、内置属性。

        上文讲到,属性是面向对象编程中的一个概念,通常在类中一个属性对应一个变量。局部变量是类中方法内的变量,根据面向对象编程的概念,我们也可以把局部变量称为局部属性。

        但是,局部变量和类的属性在定义和使用上有明显的区别,为了不把问题复杂化,我们这里就不使用局部属性这个概念。
        
        (1) 类属性

            类属性或称类变量,在类体中方法(函数)之外的变量,它是类的所有实例化对象共享的变量。类变量既可以使用 '类名.变量名' 方式调用,也可以使用 '对象.变量名' 方式调用(不推荐这种方式,见下文)。

            示例:

                #!/usr/bin/python3
                # -*- coding: UTF-8 -*-

                class Dog:
                    def display(self):
                        print('This is a dog')

                class Person:
                    count = 0
                    __count = 0
                    def __init__(self, name):
                        self.name = name

                    def display(self):
                        print('name:', self.name)

                if __name__ == "__main__":

                    dog = Dog()
                    Dog.count = 10      # 动态为 Dog 类添加类变量 count
                    print('dog.count:', dog.count)

                    person1 = Person('Python')
                    person2 = Person('Java')
                    print('Person.count:', Person.count)

                    Person.count = 1
                    print('Person.count = 1')
                    print('Person.count:', Person.count, ', person1.count:', person1.count, ', person2.count:', person2.count)

                    person1.count = 3   # 这不是给 Person 的类变量 count 赋值,而是给对象 person1 定义新的实例变量 count
                    print('person1.count = 3')
                    print('Person.count:', Person.count, ', person1.count:', person1.count, ', person2.count:', person2.count)

                    Person.count = 5
                    print('Person.count = 5')
                    print('Person.count:', Person.count, ', person1.count:', person1.count, ', person2.count:', person2.count)


            输出结果如下:

                dog.count: 10
                Person.count: 0
                Person.count = 1
                Person.count: 1 , person1.count: 1 , person2.count: 1
                person1.count = 3
                Person.count: 1 , person1.count: 3 , person2.count: 1
                Person.count = 5
                Person.count: 5 , person1.count: 3 , person2.count: 5


            注:Python 的类变量,在默认情况下可以直接读写,尽量使用 '类名.变量名' 方式调用类变量。使用 '对象.变量名' 方式调用类变量,可能会导致给对象添加同名的实例变量的误操作。实例变量和类变量可以同名,但这种情况下,对象无法调用同名的类变量,它会首选实例变量。

        (2) 实例属性
        
            实例属性或称实例变量,在类体中方法(函数)之内,以 "self.变量名=值" 的方式定义的变量。实例变量只能使用 '对象.变量名' 方式调用,无法使用 '类名.变量名' 方式调用。

            示例:

                #!/usr/bin/python3
                # -*- coding: UTF-8 -*-

                class Person:
                    name='Person'
                    def __init__(self, name):
                        self.name = name

                    def display(self):
                        print('name:', self.name)

                if __name__ == "__main__":

                    person1 = Person('Python')
                    person2 = Person('Java')

                    print('Person.name:', Person.name, ', person1.name:', person1.name, ', person2.name:', person2.name)

                    Person.name = 'Person 2'
                    person1.name = 'Python 2'
                    person2.name = 'Java 2'
                    print('Person.name:', Person.name, ', person1.name:', person1.name, ', person2.name:', person2.name)

                    person1.age = 20
                    print('person1.age:', person1.age)
                    if hasattr(Person, 'age'):
                        print('Person.age:', Person.age)
                    else:
                        print('age not in Person')
                    if hasattr(person2, 'age'):
                        print('person2.age:', person2.age)
                    else:
                        print('age not in person2')


            输出结果如下:

                Person.name: Person , person1.name: Python , person2.name: Java
                Person.name: Person 2 , person1.name: Python 2 , person2.name: Java 2
                person1.age: 20
                age not in Person
                age not in person2

            注:给对象添加实例变量,或修改对象的实例变量的值,不会影响类的其它实例化对象,也不会影响同名的类变量。hasattr() 函数用来判断属性或方法是否存在。

        (3) 局部变量
        
            在类体中方法(函数)之内,以 "变量名=值" 的方式定义的变量。

            示例:

                #!/usr/bin/python3
                # -*- coding: UTF-8 -*-

                class Person:
                    def __init__(self, name):
                        self.name = name

                    def display(self):
                        age = 20
                        print('name:', self.name, ', age:', age)

                if __name__ == "__main__":

                    person = Person('Local')

                    if hasattr(person, 'age'):
                        print('before: person.age:', person.age)
                    else:
                        print('before: age not in person')
                    person.display()
                    if hasattr(person, 'age'):
                        print('after: person.age:', person.age)
                    else:
                        print('after: age not in person')


            输出结果如下:

                before: age not in person
                name: Local , age: 20
                after: age not in person

            注:局部变量 age 的作用域就是在 disply() 之内的范围,方法执行完成后,age 即被销毁

        (4) 内置属性
        
            内置属性或称内置变量,是指在 Python 类中用双下划线开头/结尾的特殊属性,如 __name__、__doc__ 等。
            
            这些属性通常用于定义类的元数据,控制类的行为,或者在类的不同部分之间交互。使用 dir() 函数看类或对象的内置属性,运行如下代码:

                print(dir(Person))  # 查看类
                print(dir(person))  # 查看对象

            内置方法也是用双下划线开头/结尾的特殊方法,如 __call__, __del__ 等,也可以使用同样方式查看。

    4) 类中的方法

        类中的方法可分为实例方法、类方法、静态方法、。

        (1) 实例方法

            在类中定义的方法默认都是实例方法,它至少包含一个 self 参数,用于绑定调用此方法的实例对象(Python 会自动完成绑定)。

            示例:

                #!/usr/bin/python3
                # -*- coding: UTF-8 -*-

                class Person:
                    def __init__(self, name):
                        self.name = name

                    def display(self, str):
                        print(self.name, str)

                if __name__ == "__main__":

                    person = Person('Hello')
                    person.display('Java')

                    Person.display(person, 'Python')


            输出结果如下:

                Hello Java
                Hello Python

                注:可以使用实例对象调用,第一个参数无需传入对象引用;也可以使用类调用,第一个参数要传入对象引用。

        (2) 类方法
        
            类方法需要使用 @classmethod 修饰,它至少包含一个 cls 参数,用于绑定调用此方法的类本身(Python 会自动完成绑定)。类方法推荐使用类名直接调用,也可以使用实例对象来调用(不推荐使用这种方式)。

            示例:

                #!/usr/bin/python3
                # -*- coding: UTF-8 -*-

                class Person:
                    name = 'class'
                    def __init__(self, name):
                        self.name = name

                    @classmethod
                    def display(cls, str):
                        print(cls.name, str)

                if __name__ == "__main__":

                    Person.display('Python')

                    person = Person('Hello')
                    person.display('Java')


            输出结果如下:

                class Python
                class Java
    
        (3) 静态方法

            静态方法需要使用 @staticmethod 修饰,无需添加 self 参数或 cls  参数。

            示例:

               #!/usr/bin/python3
                # -*- coding: UTF-8 -*-

                class Person:

                    @staticmethod
                    def display(str):
                        print(str)

                if __name__ == "__main__":

                    Person.display('Python')

                    person = Person()
                    person.display('Java')


            输出结果如下:

                Python
                Java

    5) 类的继承

        Python 中,实现继承的类称为子类,被继承的类称为父类(也可称为基类、超类)。     

        类继承只需在定义类的时候,将父类作为参数传给子类,语法格式如下:

            class 类名(父类1, 父类2, ...):
                # 类体

            注:类定义时没有传父类参数,则默认继承 object 类,object 类是 Python 中所有类的父类。

        示例:

            #!/usr/bin/python3
            # -*- coding: UTF-8 -*-

            class Person:
                def display(self, str):
                    print(str)

            class Man(Person):
                pass

            if __name__ == "__main__":

                man = Man()
                man.display('Python')


        输出结果如下:

            Python

    6) 方法重写和多态

        方法重写就是在子类中添加与父类同名的方法,两个同名方法可以拥有不同数量的参数。

         示例:

            #!/usr/bin/python3
            # -*- coding: UTF-8 -*-

            class Person:
                def __init__(self, prefix):
                    self.prefix = prefix

                def display(self, str):
                    print(self.prefix, ':', str)

            class Man(Person):
                def __init__(self, prefix):
                    super().__init__(prefix)

                def display(self, str):
                    print(self.prefix, ':', str)

            def display(obj, str):
                obj.display(str)

            if __name__ == "__main__":

                man = Man('Man')
                man.display('Python')

                # 通过父类调用到父类 display() 函数
                Person.display(man, 'Java')

                # 多态
                person = Person('Person')
                display(person, 'polymorphic')
                display(man, 'polymorphic')


        输出结果如下:

            Man : Python
            Man : Java
            Person : polymorphic
            Man : polymorphic

        注:通过子类的对象 man 无法访问被重写的父类 display() 函数,参考上文的 '实例方法' 部分,可以通过父类 Person 调用到父类 display() 函数。

            子类 Man 的 __init__() 初始化时,把 prefix 值通过 super() 方式传给了父类 Person。

            全局函数 display(obj, str) 中,obj.display() 表现出了多态。如果 obj 是 Person 类型,就使用 Person 的 display(); 如果 obj 是 Man 类型,就是用 Man 的 display().

 

2. 模块和包

    Python 模块 (Module),是一个 Python 文件 (*.py), 可以包含多个类、函数、变量。

    函数是对 Python 代码的封装,类是对方法和属性的封装,模块是比函数、类更大范围的封装。

    1) import 语句

        import 语句用于导入 Python 标准库的模块、第三方库的模块或自定义模块,语法格式如下:

            import module1, module2 as mod2, ...

        解释说明:

            (1) import 语句可以导入一个模块,也可以同时导入多个模块,as 语句是给模块取别名;
            (2) 多行 import 语句导入同名模块,该同名模块只会被导入一次;
            (3) import 会导入模块中的所有成员(包括变量、函数、类等);

    2) from ... import 语句

        from ... import 语句用于导入指定模块内的类、函数或变量,语法格式如下:

            from module import name1, name2 as n2, ...

        解释说明:

            (1) 该语句可以导入一个模块的单个成员,也可以同时导入多个成员;
            (2) from ... import * 可以导入模块中所有成员,不推荐使用这种方式。

    3) 自定义模块

        示例,创建一个 Python 模块文件 mod.py, 代码如下:

            #!/usr/bin/python3
            # -*- coding: UTF-8 -*-

            _var1 = 5
            __var2 = 9
            data = 'Test data'

            message = 'It is custom module'

            def display(str):
                print(str)

            class Format:
                def display(self, str):
                    print(str)

            __all__ = ['message', 'display', 'Format']


            注:单下划线 “_” 或者双下划线 “__” )开头的变量 _var1、__var2, 仅在 from ... import * 导入方式时会被忽略而不导入。

                仅在 from ... import * 导入方式时,如果由 __all__ 变量,只能导入 __all__ 变量指定的成员。

        创建一个使用 mod 模块的 Python 文件 run_mod.py,和 mod.py 在同一级目录下, 代码如下:

            #!/usr/bin/python3
            # -*- coding: UTF-8 -*-

            import mod
            from mod import message, display, Format


            # import
            mod.display(mod.message)

            format = mod.Format()
            format.display('Format: ' + mod.message)

            # from ... import
            display('from ... import -> ' + message)

            format2 = Format()
            format.display('from ... import -> Format: ' + message)


        输出结果如下:

            It is custom module
            Format: It is custom module
            from ... import -> It is custom module
            from ... import -> Format: It is custom Module

    4) 包 (package)

        Python 包(package)就是一个存放多个 Python 模块的文件夹,该文件夹下必须存在一个名为 '__init__.py' (前后都是双下划线) 的文件,__init__.py 文件可以是个空文件。

        (1) 创建包

            创建一个 demo_package 目录,在 demo_package 目录下创建 3 个 Python 文件 __init__.py、mod1.py、mod2.py。

            文件 __init__.py, 内容如下:

                __all__ = ['mod1']
            
            文件 mod1.py, 内容如下:

                #!/usr/bin/python3
                # -*- coding: UTF-8 -*-

                message = 'It is custom module'

                def display(str):
                    print(str)


            文件 mod2.py, 内容如下:

                #!/usr/bin/python3
                # -*- coding: UTF-8 -*-

                def display(str):
                    print(str)


            demo_package 包的文件结构如下:

                demo_package
                    |-- __init__.py
                    |-- mod1.py
                    |-- mod2.py

        (2) 导入包

            创建一个 Python 文件 test.py 和 demo_package 在同一级目录,test.py 内容如下:

                #!/usr/bin/python3
                # -*- coding: UTF-8 -*-

                import demo_package as demoPackage  # 包的本质就是模块,可以用 import 导入
                import demo_package.mod1 as demoMod1
                import demo_package.mod2 as demoMod2

                print(demoPackage.__all__)  # 此时 demoPackage 对应 __init__.py 文件

                demoMod1.display('1 -> ' + demoMod1.message)
                demoMod2.display('2 -> ' + demoMod1.message)


            输出结果如下:

                ['mod1']
                1 -> It is custom module
                2 -> It is custom module

            修改 test.py 内容如下:

                #!/usr/bin/python3
                # -*- coding: UTF-8 -*-

                import demo_package as demoPackage  # 包的本质就是模块,可以用 import 导入
                from demo_package import *

                print(demoPackage.__all__)  # 此时 demoPackage 对应 __init__.py 文件

                mod1.display('1 -> ' + mod1.message)
                mod2.display('2 -> ' + mod2.message)


            输出结果如下:

                ['mod1']
                1 -> It is custom module
                Traceback (most recent call last):
                File "d:/pythonDemo/test.py", line 16, in <module>
                    mod2.display('2 -> ' + mod2.message)
                NameError: name 'mod2' is not defined

            注:__init__.py 文件里的 __all__ 变量,仅在 from demo_package import * 导入方式时,只允许 mod1 模块被访问,


posted @ 2024-08-23 22:34  垄山小站  阅读(190)  评论(0编辑  收藏  举报