美文网首页
python面向对象编程

python面向对象编程

作者: 像小象的云 | 来源:发表于2018-10-19 19:05 被阅读0次

    面向对象编程(一)

    1、面向过程程序设计与面向对象程序设计:

    面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度。
    面向对象的程序设计把计算机程序视为一组对象(具有相同的属性和行为)的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。
    从如下例子感受两种编程思想的差异:

    假设我们要处理一个学生的成绩:
    """
    
    #1、使用面向过程的程序设计:
    #我们首先使用一个dict来保存学生的信息
    stu1 = {"name":"Lily","score":89}
    #然后定义一个函数来处理学生的成绩
    def print_score(stu):
        print("%s:%d"%(stu["name"],stu["score"]))
    print_score(stu1)
    
    """
    2、使用面向对象的程序设计:我们首先思考的不是程序执行过程,而是学生应该被视为一个对象,
    这个对象拥有name与score两个属性,如果要打印学生成绩,我们要先创建出这个对象,然后给
    它发消息让它打印成绩
    """
    
    class Student(object):
        def __init__(self,name,score):
            self.name = name
            self.score = score
        def print_score(self):
            print("%s:%d"%(self.name,self.score))
    
    Lily = Student("Lily",89)
    Lily.print_score()
    
    2、在python中定义类

    在python中所有的数据类型都可以视为对象。在Python中可以使用class关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来。在python中定义类的关键字为class,基本格式:

    class Person(object):
        pass
    """
    关键字class其后紧跟类名Person,然后元组(object)表示继承自什么
    类(可以有多个),如果不写,默认就继承自基类object
    """
    
    3、实例化对象:
    p = Person() #实例化对象p
    print(type(p)) 
    #运行结果,可以看出p属于Person类
    <class '__main__.Person'>
    
    4、类中的方法

    类中方法包括对象方法、类方法和静态方法,类方法的定义需要使用装饰器@classmethod,静态方法的定义需要使用装饰器@staticmethod,如下:

    def foo():
        print("一般函数")
    class A(object):
        def foo(self): #对象方法
            print("对象方法")
        @classmethod
        def class_foo(cls): #类方法
            print("类方法")
        @staticmethod
        def static_foo():#静态方法
            print("静态方法")
    a = A()
    

    这里先理解一下函数中的self与cls,self与cls分别是对实例与类的绑定。对于一般的函数,我们可以这么调用foo(),他的工作与任何东西(实例,类)都无关。对于对象方法,我们在类里每次定义方法的时候都需要绑定这个实例,就是foo(self),因为对象方法的调用离不开对象,我们需要把对象传给函数,调用的时候是这样a.foo()(其实是这样foo(a)),类方法是一样,只不过它传入的是类本身。对于普通方法其实和普通方法一样,不需要对谁进行绑定,只不过调用的时候需要使用a.static_foo()或者A.static_foo()

    对象方法 类方法 静态方法
    a=A() a.foo() a.class_foo() a.static_foo()
    A 不可用 A.class_foo() A.static_foo()
    class A(object):
        def foo(self): #定义对象方法
            print(id(self),"对象方法")
        @classmethod     #定义类方法
        def class_foo(cls): 
            print(id(cls),"类方法")
        @staticmethod  #定义静态方法
        def static_foo():
            print("静态方法")
    a = A()  #实例化对象
    print(id(a))
    a.foo()  #调用对象方法
    print(id(A))
    A.class_foo()  #类调用类方法
    a.class_foo()   #对象调用类方法
    a.static_foo()  #对象调用静态方法
    A.static_foo() #类调用静态方法
    
    #执行结果  可以看出self就是对象本身,cls就是类本身
    31601264
    31601264 对象方法
    31545232
    31545232 类方法
    31545232 类方法
    静态方法
    静态方法
    
    5、类中的属性

    (1)类中的属性包括对象属性,类字段
    对象属性只对每个具体的对象有效,这个有效是这不同的对象的同一属性值可能不同,类字段归所有对象公有,值都一样。
    由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。是通过定义一个特殊的init方法来实现初始化的,这里的这些属性就是对象属性。

    class Student(object):
        def __init__(self, name, score):
            self.name = name
            self.score = score
    

    注意到init方法的第一个参数永远是self,表示创建的实例本身,因此,在init方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。有了init方法,在创建实例的时候,就不能传入空的参数了,必须传入与init方法匹配的参数,但self不需要传。

    类字段直接写在类中就行

    class A(object):
        num = 64  #这个num就是类字段了
        pass
    

    (2)访问限制
    在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑。但是,从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name、score属性

    class Student(object):
        def __init__(self, name, score):
            self.name = name
            self.score = score
    bart = Student('Bart Simpson', 59)
    print(bart.score)
    bart.score = 88  #随意改变成绩
    print(bart.score)
    
    #执行结果
    59
    88
    

    如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线,在Python中,实例的变量名如果以两个下划线开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问。

    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)
    bart.print_score()
    print("*********")
    print(bart.__score)
    
    #执行结果,无法访问bart.__score
    Bart Simpson: 59
    *********
    Traceback (most recent call last):
      File "E:/Python工程/test/1、运算符.py", line 12, in <module>
        print(bart.__score)
    AttributeError: 'Student' object has no attribute '__score'
    

    python中没有真正的私有化(没有从访问权限上去限制内容的访问),私有的原理就是在私有的属性名或者方法名前加前缀'_类名'来阻止外部直接通过带两个下划线的名字去使用属性和方法,所以上的列子中还是可以通过bart._Stedent__score来访问成绩

    注意:①在python中形如foo前后都有双下划线的名字是系统内部的名字,用来区别其他用户自定义的名字
    ②_foo这样只有前面有一个下划线的名字是一种约定,程序员用来指定私有变量的一种方式,不能使用 from model import * 的形式导入,其他访问方式一样,但是看见这种命名,你应该自觉的不去访问
    ③__foo这样前边有两个下划线的名字具有真正的意义,python解释器会解析成_classname__foo。

    6、数据封装,继承,多态

    面向对象编程的三大特点就是数据封装,继承,多态。

    数据封装

    面向对象编程的一个重要特点就是数据封装,在上面的Student类中,每个实例就拥有各自的name和score这些数据。既然Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数,这样,就把“数据”给封装起来了。这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法。这样一来,我们从外部看Student类,就只需要知道,创建实例需要给出name和score,而如何打印score,name,都是在Student类的内部定义的,这些数据和逻辑被“封装”起来了,调用很容易,但却不用知道内部实现的细节。

    继承与多态

    在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。继承的一个好处就是可使子类自动获取父类的所有属性和方法。继承的另一个好处就是多态。
    要理解什么是多态,我们首先要对数据类型再作一点说明。当我们定义一个class的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和Python自带的数据类型,比如str、list、dict没什么两样:

    class Animal(object):
        def run(self):
            print('Animal is running...')
    
    class Dog(Animal):
        def run(self):
            print('Dog is running...')
    
    class Cat(Animal):
        def run(self):
            print('Cat is running...')
    
    a = list() # a是list类型
    b = Animal() # b是Animal类型
    c = Dog() # c是Dog类型
    
    print(isinstance(a, list))
    print(isinstance(b, Animal))
    print(isinstance(c, Dog))
    print(isinstance(c, Animal))
    
    #执行结果
    True
    True
    True
    True
    

    看来a、b、c确实对应着list、Animal、Dog这3种类型。看来c不仅仅是Dog,c还是Animal!不过仔细想想,这是有道理的,因为Dog是从Animal继承下来的,当我们创建了一个Dog的实例c时,我们认为c的数据类型是Dog没错,但c同时也是Animal也没错,Dog本来就是Animal的一种!所以,在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行,Dog可以看成Animal,但Animal不可以看成Dog。

    要理解多态的好处,我们还需要再编写一个函数,这个函数接受一个Animal类型的变量:

    def run_twice(animal):
        animal.run()
        animal.run()
    #当我们传入Animal的实例时,run_twice()就打印出:
    run_twice(Animal())
    
    #当我们传入Dog的实例时,run_twice()就打印出:
    run_twice(Dog())
    
    #当我们传入Cat的实例时,run_twice()就打印出:
    run_twice(Cat())
    
    #执行结果
    Animal is running...
    Animal is running...
    Dog is running...
    Dog is running...
    Cat is running...
    Cat is running...
    

    7、使用__slots__

    正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。

    class Student(object):
        pass
    s = Student()
    s.name = 'Michael' # 动态给实例绑定一个属性
    print(s.name)
    #执行结果
    Michael
    

    但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

    class Student(object):
        __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
    s = Student()
    s.name = 'Michael' # 
    s.age = 25 # 绑定属性'age'
    s.score = 99 # 绑定属性'score'
    #执行结果
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Student' object has no attribute 'score'
    

    由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的,除非在子类中也定义一个。

    8、getter和setter

    1.什么时候需要添加对象属性的getter和setter
    如果希望在通过对象.属性获取属性的值之前,再干点儿别的事情,就可以给这个属性添加getter。
    如果希望在通过对象.属性给属性赋值之前,再干点儿别的事情,就可以给这个属性添加setter
    
    2.怎么添加setter和getter
    getter:
    a.在属性名前加_
    b.添加属性对应的getter
    @property
    def 属性名去掉_(self):
        函数体 --> 会对属性的值进行处理后,返回相应的结果(必须要有返回值)
        
    c.使用属性的值的时候,不通过带下划线的属性名去使用,而是通过没有下划线的属性去使用
    
    注意:对象.不带下划线的属性 --> 调用getter对应的函数
    
    setter:
    如果想要添加setter必须要先添加getter
    a.添加setter
    @getter名.setter
    def 属性名去掉_(self, 参数):
        做别的事情
        self.属性名 = 处理后的值
    
    """
    # 赋值时要求age的值只能在0-150之间,超过范围报错;获取age的值的时候,返回年龄值,并且返回这个年龄对应的阶段
    # class Person:
    #     def __init__(self):
    #         self.name
    #         self.age = 18
    
    # print(p1.age)  --> (18, 成年)
    # value, jieduan  = p1.age  value -> 18, jieduan -> 成年
    
    
    class Number:
        def __init__(self):
            self._value = 0
            # 0-6保存
            self._week = 3
            self.type = int
            self.id = None
    
        # _value添加getter和setter
        @property
        def value(self):
            return self._value
    
        @value.setter
        def value(self, x):
            if not -100 <= x <= 100:
                raise ValueError
            self._value = x
    
    
    
    
        # _week的getter
        @property
        def week(self):
            if self._week == 0:
                return '星期天'
            elif self._week == 1:
                return '星期一'
            elif self._week == 2:
                return '星期二'
            elif self._week == 3:
                return '星期三'
            elif self._week == 4:
                return '星期四'
            elif self._week == 5:
                return '星期五'
            elif self._week == 6:
                return '星期六'
    
        """
        isinstance(值, 类) --> 判断指定的值是否是指定类型(返回值是bool)
        """
        @week.setter
        def week(self, value):
            # 如果传的值不是整型数据
            if not isinstance(value, int):
                raise ValueError
            if not 0 <= value <= 6:
                raise ValueError
    
            self._week = value
    
    
    
    number = Number()
    number.value = 99
    print(number.week)  # number.week 实质是在通过number去调用getter对应的week方法
    number.week = 1  # number.week = 值  实质是通过number去调用setter对应的week方法
    
    
    number.value = 100
    print(number.value)
    
    9、方法重写与运算符重载
    父类方法重写
    继承后子类会拥有父类的属性和方法,也可以添加属于自己的属性和方法
    
    1.添加新的方法
    直接在子类中声明新的方法,新的方法只能通过子类来使用
    
    2.重写
    a.子类继承父类的方法,在子类中去重新实现这个方法的功能 -- 完全重写
    b.在子类方法中通过super().父类方法去保留父类对应的方法的功能
    
    3.类中的函数的调用过程
    类.方法(), 对象.方法()
    
    先看当前类是否有这个方法,如果有就直接调用当前类中相应的方法;
    如果没有就去当前的父类中去看有没有这个方法,如果有就调用父类的这个方法;
    如果父类中也没有这个方法,就去父类的父类中找,依次类推直到找到为止。
    如果找到基类object,还没有找到这个方法,程序才异常
    """
    class Person:
    
        def __init__(self, name=''):
            self.name = name
    
        def eat(self, food):
            # self = super()
            print('%s在吃%s' % (self.name,  food))
    
        @staticmethod
        def run():
            print('人在跑步')
    
        @classmethod
        def get_up(cls):
            print('===========')
            print('洗漱')
            print('换衣服')
    
    
    
    class Staff(Person):
        pass
    
    class Student(Person):
    
        def study(self):
            print('%s在学习' % self.name)
    
        def eat(self, food):
            # super():当前类的父类的对象
            print('对象方法:',super())
            super().eat(food)
            print('喝一杯牛奶!')
    
        @staticmethod
        def run():
            print('学生在跑步')
    
        @classmethod
        def get_up(cls):
            # super() -> 获取当前类的父类
            # super().get_up() ->调用父类的get_up方法
            print('类方法', super())
            super().get_up()  # 可以保留父类get_up的功能
            print('背书包')
    
    
    p1 = Person()
    Person.run()
    Person.get_up()
    p1.name = '小红'
    p1.eat('面条')
    
    
    stu1 = Student()
    stu1.study()
    Student.run()
    Student.get_up()
    
    stu1.name = '小花'
    stu1.eat('面包')
    
    运算符重载
    运算符重载: 通过实现类响应的魔法方法,来让类的对象支持相应的运算符(+, -, > ,< 等)
    
    值1 运算符 值2 ---> 值1.魔法方法(值2)
    """
    
    10 > 20   # int类,实现 > 对应的魔法方法 __gt__
    10 < 20
    ['12', 2] > ['abc' , 1, 34]  # list类,实现 > 对应的魔法方法 __gt__
    
    10 / 20   # __truediv__
    
    20 % 10
    
    
    import copy
    import random
    
    class Student:
        def __init__(self, name, age, score):
            self.name = name
            self.age = age
            self.score = score
    
        #  __gt__就是 > 对应的魔法方法
        def __gt__(self, other):
            # self -> 指的是大于符号前面的值, other -> 指的是>符号后面的值
            return self.score > other.score
    
        # __lt__是 < 对应的魔法方法
        # 注意:gt和lt只需要实现一个就可以了
        def __lt__(self, other):
            return self.score < other.score
    
        def __add__(self, other):
            return self.score + other.score
    
        def __mul__(self, other: int):
            result = []
            for _ in range(other):
                result.append(copy.copy(self))
            return result
    
    
    stu1 = Student('小哈', 23, 89)
    stu2 = Student('小🌺', 19, 90)
    print(stu1 > stu2)
    print(stu1 < stu2)
    
    print(stu1 + stu2)
    
    students = stu1*10
    print(students)
    students[0].name = '小明'
    
    
    class Person:
        def __init__(self, name='张三', age=0):
            self.name = name
            self.age = age
    
        def __mul__(self, other: int):
            result = []
            for _ in range(other):
                result.append(copy.copy(self))
            return result
    
        def __gt__(self, other):
            return self.age > other.age
    
    
        # 定制打印格式
        def __repr__(self):
            return str(self.__dict__)[1:-1]
    
    
    # 同时创建10个人的对象
    persons = Person()*10
    # persons = 10 * Person()
    # print(persons)
    
    for p in persons:
        p.age = random.randint(15, 35)
    
    print(persons)
    
    # 列表元素是类的对象,使用sort对列进行排序
    persons.sort()
    print(persons)
    
    print(max(persons))
    
    
    class Dog:
        def __mul__(self, other):
            pass
    
    dog1 = Dog()
    dog1 * 4
    

    相关文章

      网友评论

          本文标题:python面向对象编程

          本文链接:https://www.haomeiwen.com/subject/sxtdzftx.html