• 8. 类与对象


    术语简介

    • 类:用来描述具有相同属性和方法的对象的集合。类定义了集合中每个对象共有的属性和方法。对象是累的实例。
    • 类变量(属性):类变量在整个实例化的对象中是公用的。类变量定义在类中,且在方法之外。类变量通常不作为实例变量使用。类变量也称作属性。
    • 方法:类中定义的函数。
    • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
    • 实例化:创建一个类的实例、类的具体对象。
    • 数据成员:类变量或实例变量用于处理类及其实例对象的相关数据。

    类的定义和使用

    类的定义语法格式如下:

    class MyClass(object):
        i=123
        def f(self):
            return 'Hello World'
    
    class ClassName(object):    #类名,驼峰命名
        属性1                   #类的属性
        属性2
        ...
        方法1                   #类的方法
        方法2
        ...
    

    python中定义类使用class关键字,class后面紧接着类名,如示例中的MyClass,类名通常是大写开头的单词(一般使用驼峰命名,要求见名知意);紧接着是(object),表示该类是从哪个类继承下来的。通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。类包含属性(相当于函数中的语句)和方法(方法大体可以理解成函数)。

    • 注意:在类中定义方法的形式和函数差不多,但不称为函数,而称为方法。方法的调用需要绑定到特定的对象上,而函数不需要。

    类的使用:

    class MyClass(object):
        i=123
        def f(self):
            return 'Hello World'
    
    use_class=MyClass()
    print('调用类的属性:',use_class.i)
    print('调用类的方法:',use_class.f())
    
    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    调用类的属性: 123
    调用类的方法: Hello World
    

    由输入代码中的调用方式可知,类的使用比函数调用多了几个操作,调用类时需要执行如下操作:

    use_class=MyClass()
    

    这步叫做类的实例化,即创建一个类的实例。此处得到的 use_class 变量称为类的具体对象。再看后面两行的调用:

    print('调用类的属性:',use_class.i)
    print('调用类的方法:',use_class.f())
    

    这里第一行后面的 use_class.i 用于调用类的属性,也就是我们前面说的类变量。第二行后的 use_class.f() 用于调用类的方法。
    在上面的例子中,在类中定义 f()方法时带了一个self参数,该参数在方法中并没有被调用,是否可以不要呢?调用f()方法时没有传递参数,是否表示参数可以传递也可以不传递?
    对于在类中定义方法的要求:在类中定义方法时,第一个参数必须是self。 第一个参数外,类的方法和普通函数并没有什么区别。
    对于在类中调用方法的要求:要调用一个方法,在实例变量上直接调用即可。除了self不用传递,其他参数正常传入。
    类对象直尺两种操作,即属性引用和实例化。属性引用的标准语法如下:

    obj.name
    

    语法中boj代表类对象,name代表属性。

    构造函数(初始化函数)

    class MyClass(object):
        i=123
        def __init__(self,name):
            self.name=name
    
        def f(self):
            return 'hello,'+self.name
    
    use_class=MyClass('xiaomeng')
    print('调用类的属性:',use_class.i)
    print('调用类的方法:',use_class.f())
    
    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    调用类的属性: 123
    调用类的方法: hello,xiaomeng
    

    若类的实例化语句写法和之前一样,即:

    use_class=MyClass()
    

    程序执行结果如下:

    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    Traceback (most recent call last):
      File "D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py", line 11, in <module>
        use_class=MyClass()
    TypeError: __init__() missing 1 required positional argument: 'name'
    

    从代码和输出结果可以看到,实例化MyClass类时调用了__init__()方法。这里就奇怪了,我们在代码中并没有指定调用__init__()方法,怎么会报__init__()方法错误呢?
    在python中,init()方法是一个特殊方法,在对象实例化时会被调用。init()的意思是初始化,是initialization的简写。这个方法的书写方式是:先输入两个下划线,后面接着init,再接着两个下划线。这个方法也叫构造方法。在定义类时,若不显式地定义一个__init__()方法,则程序默认调用一个无参的__init__()方法。
    比如下面两段代码的使用效果是一样的:

    class DefualtInit(object):
        def __init__(self):
            print('类实例化时执行我,我是__init__方法。')
    
        def show(self):
            print('我是类中定义的方法,需要通过实例化对象调用。')
    
    test=DefualtInit()
    print('类实例化结束。')
    test.show()
    
    执行结果:
    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    类实例化时执行我,我是__init__方法。
    类实例化结束。
    我是类中定义的方法,需要通过实例化对象调用。
    
    class DefualtInit(object):
        def show(self):
            print('我是类中定义的方法,需要通过实例化对象调用。')
    
    test=DefualtInit()
    print('类实例化结束。')
    test.show()
    
    执行结果:
    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    类实例化结束。
    我是类中定义的方法,需要通过实例化对象调用。
    

    由上面两段代码的输出结果可以看到,当代码中定义了__init__()方法时,实例化类时会调用该方法;若没有定义__init__()方法,实例化类时也不会报错,此时调用默认的__init__()方法。
    在python中定义类时若没有定义构造方法(init()方法),则在类的实例化时系统调用默认的构造方法。此外,init()方法可以有参数,参数通过__init__()传递到类的实例化操作上。
    既然__init__()方法是python中的构造方法,那么是否可以在一个类中定义多个构造方法呢?先看如下3段代码:
    代码一

    class DefualtInit(object):
        def __init__(self):
            print('我是不带参数的__init__方法。')
    
    DefualtInit()
    print('类实例化结束。')
    
    执行结果:
    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    我是不带参数的__init__方法。
    类实例化结束。
    

    只有一个__init__()方法时,实例化类没有什么顾虑。



    代码二

    class DefualtInit(object):
        def __init__(self):
            print('我是不带参数的__init__方法。')
    
        def __init__(self,param):
            print('我是一个带参数的__init__方法,参数值为:',param)
    
    DefualtInit('hello')
    print('类实例化结束。')
    
    执行结果如下:
    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    我是一个带参数的__init__方法,参数值为: hello
    类实例化结束。
    

    由执行结果可以看到,调用的时带了一个param参数的构造方法,若把类的实例化语句改为:

    DefualtInit()
    

    执行结果为:

    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    Traceback (most recent call last):
      File "D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py", line 10, in <module>
        DefualtInit()
    TypeError: __init__() missing 1 required positional argument: 'param'
    

    或更改为:

    DefualtInit('hello','world')
    

    执行结果:

    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    Traceback (most recent call last):
      File "D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py", line 10, in <module>
        DefualtInit('hello','world')
    TypeError: __init__() takes 2 positional arguments but 3 were given
    

    由执行结果可以看到,实例化类时只能调用带两个占位参数的构造方法,调用其他构造方法都会报错。



    代码三

    class DefualtInit(object):
        def __init__(self,param):
            print('我是一个带参数的__init__方法,参数值为:',param)
    
        def __init__(self):
            print('我是不带参数的__init__方法。')
    
    DefualtInit()
    print('类实例化结束。')
    
    执行结果如下:
    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    我是不带参数的__init__方法。
    类实例化结束。
    

    由执行结果看到,调用的构造方法除了self外,没有其他参数。若把类的实例化语句更改为:

    DefualtInit('hello')
    

    执行结果如下:

    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    Traceback (most recent call last):
      File "D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py", line 10, in <module>
        DefualtInit('hello')
    TypeError: __init__() takes 1 positional argument but 2 were given
    

    或更改为:

    DefualtInit('hello','world')
    

    执行结果如下:

    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    Traceback (most recent call last):
      File "D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py", line 10, in <module>
        DefualtInit('hello','world')
    TypeError: __init__() takes 1 positional argument but 3 were given
    

    由执行结果可以看到,实例化类时只能调用带一个占位参数的构造方法,调用其他构造方法都会报错。
    由以上几个示例我们得知:一个类中可以定义多个构造方法,但实例化类时只实例化最后的构造方法,即后面的构造方法会覆盖前面的构造方法,并且需要根据最后一个构造方法的形式进行实例化。建议一个类中只定义一个构造方法(初始化函数)。

    PS
    我们可以根据情况选择是否将参数放在构造函数中:

    class Math:
        def __init__(self,a,b):
            self.a=a
            self.b=b
        def add(self):
            print(self.a+self.b)
    
    #依次求1+2,3+4,5+6的和
    test=Math(1,2)
    test.add()
    test=Math(3,4)
    test.add()
    test=Math(5,6)
    test.add()
    
    class Math:
        def add(self,a,b):
            print(a+b)
    
    #依次求1+2,3+4,5+6的和
    test=Math()
    test.add(1,2)
    test.add(3,4)
    test.add(5,6)
    

    显然,以上情况第二种更为方便。
    再看如下情况:

    class Math:
        def __init__(self,a,b):
            self.a=a
            self.b=b
        def add(self):
            print(self.a+self.b)
        def sub(self):
            print(self.a-self.b)
        def mul(self):
            print(self.a*self.b)
        def div(self):
            print(self.a/self.b)
    
    #依次求1+2,1-2,1*2,1/2的结果
    test=Math(1,2)
    test.add()
    test.sub()
    test.mul()
    test.div()
    
    class Math:
        def add(self,a,b):
            print(a+b)
        def sub(self,a,b):
            print(a-b)
        def mul(self,a,b):
            print(a*b)
        def div(self,a,b):
            print(a/b)
    
    #依次求1+2,1-2,1*2,1/2的结果
    test=Math()
    test.add(1,2)
    test.sub(1,2)
    test.mul(1,2)
    test.div(1,2)
    

    以上情况,第二种方法就比较方便。

    类的继承

    面向对象变成带来的好处之一是代码的重用,实现重用的方法之一是通过继承机制,继承完全可以理解成类之间类型和子类型的关系。

    在面向对象程序设计中,当我们定义一个class时,可以从某个现有的class继承,定义的新class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)

    继承的定义如下:

    class DerivedClassName(BaseClassName)
        <statement-1>
        ...
        <statement-N>
    

    需要注意:继承语法class子类名(基类名),其中基类名(被继承的类名)写在括号里,基本类是在定义类时,在元组中指明的。

    python中,继承有以下特点:

    1. 在继承中,基类的构造方法(init()方法)不会被自动调用,需要在子类的构造方法中专门调用。
    2. 在调用基类的方法时需要加上基类的类名前缀,并带上self参数变量。区别于在类中调用普通函数时不需要带self参数。
    3. 在python中,首先查找对应类型的方法,如果在子类中找不到对应的方法,才到基类中逐个查找。
    class Animal(boject):
        def run(self):
            print('Animal is running...')
    

    上面定义了一个名为Animal的类,类中定义了一个 run()方法直接输出(没有显式定义__init__()方法,会调用默认的构造方法)。在编写Dog和Cat类时,可以直接从Animal类继承:

    class Dog(Animal):
        pass
    
    class Cat(Animal):
        pass
    

    在这段代码中,对于Dog来说,Animal就是它的父类;对于Animal来说,Dog就是它的子类。Cat和Dog类似。
    继承有什么好处?
    继承最大的好处就是子类获得了父类全部非私有的功能。由于在Animal中定义了非私有的run()方法,因此作为Animal的子类,Dog和Cat什么方法都没有定义,自动拥有父类中的run()方法。

    dog=Dog()
    dog.run()
    cat=Cat()
    cat.run()
    
    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    Animal is running...
    Animal is running...
    

    由执行结果可以看到,子类中没有定义任何方法,但都成功执行了run()方法。

    当然,子类可以拥有自己的一些方法,比如在Dog类中增加一个eat方法:

    class Dog(Animal):
        def eat(self):
            print('Eating...')
    
    dog=Dog()
    dog.run()
    dog.eat()
    
    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    Animal is running...
    Eating...
    

    由执行结果可以看到,既执行了父类的方法,也执行了自己定义的方法。
    子类不能继承父类中的私有方法,也不能调用父类的私有方法。

    class Animal():
        def run(self):
            print('Animal is running...')
    
        def __run(self):            #私有方法
            print('I am a private method')      
    
    class Dog(Animal):
        def eat(self):
            print('Eating...')
    
    dog=Dog()
    dog.__run()
    
    
    D:Pythonpython.exe D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py
    Traceback (most recent call last):
      File "D:/Work/Tools/python_workspace/python_2017/class_basic/temp3.py", line 14, in <module>
        dog.__run()
    AttributeError: 'Dog' object has no attribute '__run'
    

    由执行结果可以看到,子类不能调用父类的私有方法,子类虽然继承了父类,但是调用父类的私有方法相当于从外部调用类中的方法,因而调用不成功。
    对于父类中扩展的非私有方法,子类可以拿来即用。子类可以乐基获得父类增加的非私有方法。
    集成可以一级一级继承下来,就好比爷爷到爸爸再到儿子的关系。所有类最终都可以追溯到根类object,这些继承关系看上去就像是一颗倒着的树。如下所示:

    object
    Animal
    Plant
    Dog
    Cat
    Shepherd
    Husky
    Tree
    Flower
  • 相关阅读:
    面试官:软件测试没搞懂这些,哪里来的自信投简历? 刁钻问得高频的面试题(含答案)
    软件测试行业的职业素养?——《高级软件测试-高级软件测试分析师》第一章
    什么?你正在学web自动化测试?那这些Selenium的基本操作你了解过吗?
    没想到 Google 排名第一的编程语言,为什么会这么火?
    为什么大家都在用Fiddler?
    看了很多文章,就这篇说明白了什么是接口测试(含视频教程)
    Win 10 安裝 Docker 及相關設置
    Dynamics CRM
    Dynamics CRM
    Dynamics CRM
  • 原文地址:https://www.cnblogs.com/xiaotufei/p/13338452.html
Copyright © 2020-2023  润新知