zoukankan      html  css  js  c++  java
  • Python学习笔记七 面向对象编程

    参考教程:廖雪峰官网https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000

    面向对象编程

    面向对象编程——Object Oriented Programming ,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。

    通过一个实例来对比面向过程和面向对象程序的区别。

    例如要处理学生的成绩,按面向过程的方式,则需要先定义学生的成绩,再通过一个函数把信息打印出来:

    std1 = { 'name': 'Michael', 'score': 98 }
    std2 = { 'name': 'Bob', 'score': 81 }
    def print_score(std):
        print('%s: %s' % (std['name'], std['score']))
    
    print_score(std1)
    print_score(std2)

    如果采用面向对象的程序设计思想,思考的侧重点不是程序的执行流程。首先对于学生的分数、姓名的处理可以通过设计一种数据类型去实现,这种数据类型拥有姓名、分数这两个属性,其实这就对应了对象和属性的特性,并且我们可以给这个对象设计其特有的函数(方法)。

    class Student(object):
          def __init__(self,name,score):
                self.name=name
                self.score=score
          def print_score(self):
                print('%s: %s' % (self.name, self.score))
    bart = Student('Bart Simpson', 59)
    lisa = Student('Lisa Simpson', 87)
    bart.print_score()
    lisa.print_score()

    对于上例,Student就是一个类(class),而bart和lisa就是这个类的两个实例(instance)对象。

    一、类和实例

    面向对象最重要的概念就是类(Class)和实例(Instance),比如上例中的Student就是一个类,而其中bart和lisa就是依据这个类创建出来的具体对象,即实例。

    类名通常用大写字母开头表示,参数表示这个类是从哪个类继承下来的,如果没有合适的继承类,就使用object类,object类是所有类都会继承的类。类的实例通过“类名()"创建。

    类的定义中可以通过__init__方法给类创建其属性,如:

    #第一个参数表示创建的实例本身
    def __init__(self,name,score):
          self.name=name
          self.score=score

    而在创建实例的时候,不需要传递第一个self参数,Python解释器会自行把实例变量传进去:

    bar=Student('Bart Simpson',60)

    通过创建上面的实例对象bart之后,就可以用bart.name/bart.score去访问该实例的属性。

    而对于类来说,属性的访问,包括一些常用的操作都可以在类定义代码中统一实现,这也称为类的方法。如给上面的Student类创建一个输出其属性的方法:

    def print_score(self):
            print('%s: %s' % (self.name, self.score))

    在实例对象调用该方法的时候也不需要传递self参数:

    bart.print_score()

    需要注意的是,和静态语言不同,Python允许对实例对象绑定任何数据:

    #创建两个Student类的实例
    bart=Student('Bart',60)
    lisa=Student('Lisa',80)
    #给bart绑定age属性
    bart.age=12
    #输出12
    print(bart.age)
    #报错,提示Student类没有age属性
    print(lisa.age)

    二、访问限制

    为保证属性不被外部访问,可以在类的定义中对于需要保护的属性名前面加上'__',这样这个属性就变成了私有变量(private),只有内部可以访问。

    修改后的Student类定义如下:

    class Student(object):
    
        def __init__(self, name, score):
            self.__name = name
            self.__score = score
    
        def print_score(self):
            print('%s: %s' % (self.__name, self.__score))

    修改后就无法访问实例对象的__name和__score属性了。

    如果外部需要访问这两个属性,那可以增加get_name和get_score方法:

    def get_name(self):
            return self.__name
    
    def get_score(self):
            return self.__score

    如果更进一步还可以让外部通过一些特定的方法可以修改属性值,那么就需要给类增加set方法:

    def set_name(self, name):
            self.__name = name
    def set_score(self, score):
            self.__score = score

    需要注意的是,在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name____score__这样的变量名。

    有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

    双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量.

    另外对于上例中的私有变量__name和__score要记得它们实质上的名称是_Student__name和_Student__score;所以在外部对实例对象设置__name是可行的,但只是给该实例增加了一个名为__name的属性。而通过类方法get_name()可以发现实际属性值并没有变化。

    >>> bart = Student('Bart Simpson', 59)
    >>> bart.get_name()
    'Bart Simpson'
    >>> bart.__name = 'New Name' # 设置__name变量!
    >>> bart.__name
    'New Name'
    >>> bart.get_name() # get_name()内部返回self.__name
    'Bart Simpson

    练习:

    '''
    请把下面的Student对象的gender字段对外隐藏起来
    用get_gender()和set_gender()代替,并检查参数有效性:
    '''
    # -*- coding: utf-8 -*-
    class Student(object):
        def __init__(self, name, gender):
            self.name = name
            self.__gender = gender
        def get_gender(self):
            return self.__gender
        def set_gender(self,gender):
            self.__gender=gender
    
    # 测试:
    bart = Student('Bart', 'male')
    if bart.get_gender() != 'male':
        print('测试失败!')
    else:
        bart.set_gender('female')
        if bart.get_gender() != 'female':
            print('测试失败!')
        else:
            print('测试成功!')

     三、继承和多态

    在OOP程序设计中,在定义一个类的时候,可以在参数中指定这个类所继承的已存在的类,比如之前的Student继承了object类。新定义的类称为子类(Subclass),而被继承的类称为基类、父类或超类(Base class、Super class)。

    比如已有如下的一个Animal类:

    class Animal(object):
        def run(self):
            print('Animal is running...')

    我们可以编写其他的类作为这个Animal类的子类,只需要在定义类时将参数设为'Animal':

    class Dog(Animal):
        pass
    class Cat(Animal):
        pass
    
    Dog().run()
    mycat=Cat()
    mycat.run()

    这样Dog和Cat类也自动拥有了run()这个方法。这里Cat和Dog类就是Animal的子类,而Animal就是它们的父类。

    但我们也可以发现,在Dog和Cat的实例对象调用run()时候,输出的内容仍然是父类定义的'Animal is running...',这里我们也可以给Dog类和Cat类设置同名的run()方法:

    class Dog(Animal):
        def run(self):
            print('Dog is running...')
    class Cat(Animal):
        def run(self):
            print('Cat is running...')
    
    Dog().run()
    mycat=Cat()
    mycat.run()

    这样输出就变成了:

    Dog is running...
    Cat is running...

    当子类和父类拥有同名方法时候,在子类中的方法就“覆盖”了父类的方法,在子类的实例对象调用这个方法时也就直接使用了子类定义的方法。

    当定义了一个类的时候,其实在当前的Python运行环境里就是多了一种数据类型,这可以通过isinstance()判断:

    print(isinstance(10,int))
    print(isinstance('abc',str))
    print(isinstance(Dog(),Animal))
    print(isinstance(Cat(),Animal))
    print(isinstance(Dog(),Dog))
    print(isinstance(Cat(),Cat))
    
    '''
    输出:
    True
    True
    True
    True
    True
    True
    '''

    但要注意下面这种情况:

    #对于通过父类创建的实例对象
    #其数据类型是不属于该父类的子类的
    
    #False
    print(isinstance(Animal(),Dog))

    而对于子类创建的实例对象,其数据类型既是子类,也可以是父类。这就是多态的一种理解。

    要充分理解多态的好处,可以看下面这个例子:

    def run_twice(animal):
        animal.run()
        animal.run()
    
    #当传入Animal类的实例时:
    run_twice(Animal())
    '''输出:
    Animal is running...
    Animal is running...
    '''
    #当传入Dog类或Cat类的实例时:
    run_twice(Dog())
    '''输出:
    Dog is running...
    Dog is running...
    '''
    #如果新增一个类
    class Sheep(Animal):
        pass
    run_twice(Sheep())
    '''输出:
    Animal is running...
    Animal is running...
    '''
    
    #我们可以发现并不需要修改run_twice方法,就可以引用sheep的实例
    #这是因为run_twice方法接收的是Animal类型的数据,而sheep是其子类
    #更进一步的实质是:Animal类型具备run方法,所以其子类都有这个方法
    #所以实质上在动态语言中,对于run_twice这样的方法只需要参数具备run方法即可,如下实例
    
    class X(object):
        def run(self):
            print('X is running')
    
    run_twice(X())
    '''输出:
    X is running
    X is running
    '''

    四、实例属性和类属性

    class Student(object):
        def __init__(self,name):
            self.name=name
    
    s=Student('bob')
    #输出'bob'
    print(s.name)
    s.score=100
    #输出100
    print(s.score)
    #报错:Student类没有score属性
    print(Student('Aly').score)

    除了在类的__init__()方法中给类设置属性外,也可以在类的定义中直接设置:

    class Student(object):
        def __init__(self,name):
            self.name=name
        score=98
    
    #可以直接通过类名访问score属性
    print(Student.score)
    #也可以通过类的实例访问
    print(Student('Bob').score)

    此时如果给类的实例对象赋予一个同名的属性,则会覆盖掉类的属性:

    anny=Student('Anny')
    anny.score=100
    print(anny.score)  #输出100,此时类属性score被覆盖
    henry=Student('Henry')
    print(henry.score)  #输出98,此时为继承类的属性
    
    #给henry增加score属性
    henry.score=99
    print(henry.score)  #输出99,此时类属性score被覆盖
    
    #实例中增加的属性可以用del删除
    del henry.score
    print(henry.score)  #输出98,此时又继承了类的属性

    练习:

    '''
    为了统计学生人数,可以给Student类增加一个类属性
    每创建一个实例,该属性自动增加
    '''
    class Student(object):
        count=0
        def __init__(self,name):
            self.__name=name
            self.set_count()
        def set_count(self):
            Student.count=Student.count+1
            
    # 测试:
    if Student.count != 0:
        print('测试失败!')
    else:
        bart = Student('Bart')
        if Student.count != 1:
            print('测试失败!')
        else:
            lisa = Student('Bart')
            if Student.count != 2:
                print('测试失败!')
            else:
                print('Students:', Student.count)
                print('测试通过!')
    
            
  • 相关阅读:
    添加自动生成备注模板
    eclipse安装go插件
    CentOS7使用minikube搭建kubernetes集群
    CentOS7 设置控制台分辨率
    CentOs7安装源设置
    Java线程死锁
    keycloak +docker-compose+mysql 启动配置
    Illegal key size or default parameters
    Springboot+Spring secuirty 前后端分离后台菜单权限设计
    Springboot +vue 实现导出功能
  • 原文地址:https://www.cnblogs.com/tsembrace/p/8605073.html
Copyright © 2011-2022 走看看