面对对象

面对对象

一、面对对象介绍

面对对象编程核心是“对象”二字,对象指的是盛放相关的数据与功能的容器

基于该思想编写程序就在创造一个个的容器来把相关的东西盛到一起

优点:拓展性强

缺点:加大了编程的复杂度

二、类与对象

 面对对象编程可以把对象的数据放在一个字典里,一个字典就是一个对象,也可以在外面定义一个函数,把函数名也放在字典里,这个字典有数据和功能,就是一个对象。

但是这样的对象太多了之后就会显得很乱,所有我们可以把他们其中相同的数据和功能函数放在一块,形成新的一个字典,在原来的字典中指向这个共同的字典。

显然这样写,当对象多的时候都找不到你想找的对象,所以python中有专门的语法来制造对象,这就是类。

类是用来解决对象之间代码冗余的问题的。

类定义阶段会发生3件事:

1.会执行类体代码

2.会产生一个类的名称空间,用来将类体代码运行过程中产生的名字都丢进去

3.将名称空间的内存地址绑定给类名

三、初始化构造方法

class Student:


    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        self.courses = []


    def choose_course(self, course):
        self.courses.append(course)
       

调用类也发生了三件事:

1.会创建空对象(调用__new__方法来创建的空对象)

2.会自动触发类中__init__函数的运行,__init__()完成对象的初始化

3.返回该对象,赋值给调用的变量名

四、属性查找

类中有两种属性

1.数据属性

2.函数属性

但是类中的属性其实是为对象准备的

1.类的属性是直接共享给所有对象用的

2.类的函数属性是绑定给对象用的

五、绑定方法与非绑定方法

类中定义的含函数分两大类

一、绑定方法:绑定给谁就应该由谁来调用,谁来调用就会将自己当作第一个参数自动传入绑定给对象或类

二、非绑定方法:不与任何人绑定,意味着谁都可以来调用,但是无论谁来调用就是一个普通函数没有自动传参的效果

类中的函数和数据是默认绑定给对象的,但是可以通过加一个装饰器来改变这个绑定。

1.@classmethod

类方法:在函数上加上这个装饰器后,这个函数就是绑定给类使用的,所以第一个传入的值就是来调用这个函数的类。

 

2.@staticmethod

静态方法:加上这个装饰器的函数不会自动传入参数,就是一个普通的函数,谁都可以来调,但是如果需要参数的话必须要传参。

六、隐藏属性

隐藏属性的真正目的就是为了不让使用者在类的外部直接操作属性。

隐藏属性的语法是在属性名前加__,__开头的属性的隐藏有四个特点:

1.并不是真正的隐藏,只是一种语法意义上的变形

2.该变形操作只是在类定义阶段扫描语法时发生一次,类定义之后__开头的属性不会变形

3.该隐藏对外不对内

 

当属性前有__被隐藏时,__会变成 ‘_类名__’ 这种形式,在类中调用的属性时也会变形,所以在类中调用就没有什么问题。

隐藏属性有一个装饰器@property,这个装饰器会让调用者在调用函数时可以像调用属性一样调用。也就是说把函数隐藏成数据属性。

class People:
    def __init__(self,name,height,weight):
        self.name = name
        self.height = height
        self.weight = weight

    @property
    def bmi(self):
        return self.weight / (self.height ** 2)


obj = People('egon',1.84,80)
print(obj.bmi)

这种装饰器也可以对数据更改和删除。

class People:
    def __init__(self,name,age):
        self.__name = name
        self.age = age

    @property
    def name(self):
        return "名字:%s" %self.__name

    @name.setter
    def name(self,v):
        if type(v) is not str:
            print("名字必须是str类型小垃圾")
            return
        self.__name = v

    @name.deleter
    def name(self):
        del self.__name
        print('删除成功')


obj = People('egon',18)

print(obj.name)
obj.name = 123
del obj.name

七、继承

1.什么是继承

继承是一种新建子类的方式,新建的类称之为子类/派生类,被继承的称之为父类/基类

子类会遗传父类的属性

2.为何要用继承

类是解决对象之间冗余问题的

继承可以解决类之间的冗余问题

3.如何继承

在Python中支持多继承

在Python3中如果一个类没有继承任何父类,那么默认继承object类

但凡是继承了object类的子类,以及该子类的子子孙孙类都能用到object内的功能,称之为新式类

没有继承了object类的子类,以及该子类的子子孙孙类都用不到object内的功能,称之为经典类

只有在Python2中才分新式类和经典类,Python3中都是新式类

那么继承是怎么解决类与类之间的冗余问题的呢,就是把一样的属性和函数放到一个父类中,然后继承这个父类。

class People:
    school='清华大学'

    def __init__(self,name,sex,age):
        self.name=name
        self.sex=sex
        self.age=age

class Student(People):
    def choose(self):
        print('%s is choosing a course' %self.name)

class Teacher(People):
    def teach(self):
        print('%s is teaching' %self.name)

如果之类还有自己的属性的话就要方法重用:

>>> class People:
...     school='清华大学'
...     
...     def __init__(self,name,sex,age):
...         self.name=name
...         self.sex=sex
...         self.age=age

>>> class Teacher(People):
...     def __init__(self,name,sex,age,title):
...         People.__init__(self,name,age,sex) #调用的是函数,因而需要传入self
...         self.title=title
...     def teach(self):
...         print('%s is teaching' %self.name)

也可以用super():调用super()会得到一个特殊的对象,该对象专门用来引用父类的属性,且严格按照MRO规定的顺序向后查找

>>> class Teacher(People):
...     def __init__(self,name,sex,age,title):
...         super().__init__(name,age,sex) #调用的是绑定方法,自动传入self
...         self.title=title
...     def teach(self):
...         print('%s is teaching' %self.name)

八、属性查找

python到底是如何实现继承的呢? 对于你定义的每一个类,Python都会计算出一个方法解析顺序(MRO)列表,该MRO列表就是一个简单的所有基类的线性顺序列表。

python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。 而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
1.由对象发起的属性查找,会从对象自身的属性里检索,没有则会按照对象的类.mro()规定的顺序依次找下去,
2.由类发起的属性查找,会按照当前类.mro()规定的顺序依次找下去,

如果是多继承的话就要分经典类还是新式类:

经典类是深度优先:

class G: # 在python2中,未继承object的类及其子类,都是经典类
    def test(self):
        print('from G')

class E(G):
    def test(self):
        print('from E')

class F(G):
    def test(self):
        print('from F')

class B(E):
    def test(self):
        print('from B')

class C(F):
    def test(self):
        print('from C')

class D(G):
    def test(self):
        print('from D')

class A(B,C,D):
    # def test(self):
    #     print('from A')
    pass

obj = A()
obj.test() # 如上图,查找顺序为:obj->A->B->E->G->C->F->D->object
# 可依次注释上述类中的方法test来进行验证,注意请在python2.x中进行测试

 

新式类是广度优先:

class G(object):
    def test(self):
        print('from G')

class E(G):
    def test(self):
        print('from E')

class F(G):
    def test(self):
        print('from F')

class B(E):
    def test(self):
        print('from B')

class C(F):
    def test(self):
        print('from C')

class D(G):
    def test(self):
        print('from D')

class A(B,C,D):
    # def test(self):
    #     print('from A')
    pass

obj = A()
obj.test() # 如上图,查找顺序为:obj->A->B->E->C->F->D->G->object
# 可依次注释上述类中的方法test来进行验证

 10、mixin机制

继承表达的关系:is-a

所以多继承时,应该只有一个主类,不然代码就会变得很乱,但是Python又是一个支持多继承的语言。

这就要用到mixin机制,mixin机制其实只是一种命名规则,在我们用多继承时,除了一个主类继承了大部分属性之外,还可以继承mixin相关的一些功能,在括号中的父类中,mixin要写在左边,最右边的就是我们继承的主类。

11、组合

组合也是解决类与类之间的代码冗余问题,但是组合表达的是has-a的关系:一个对象拥有一个属性,该属性值指向另一个对象

比如给一个学生和老师加一个课程:

class People:
    school = "SH"

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

class Course:
    def __init__(self,name,period,price):
        self.name = name
        self.period = period
        self.price = price

    def tell_info(self):
        print('%s:%s:%s' %(self.name,self.period,self.price))

class Student(People):
    def __init__(self, name, age, gender, courses=None):
        super().__init__(name, age, gender,)
        if courses is None:
            courses = []
        self.courses = courses



    def choose_course(self, course):
        self.courses.append(course)
        print("学生%s 选课成功%s" % (self.name, self.courses))


class Teacher(People):
    def __init__(self, name, age, gender, level):
        super().__init__(name, age, gender,)
        self.level = level

    def score(self, stu_obj, num):
        stu_obj.num = num
        print('老师%s 为学生%s 打分%s' % (self.name, stu_obj.name, num))



python = Course("python全栈开发","6mons",30000)


stu1 = Student('tom',18,'male')
tea1 = Teacher('lili', 28, 'female', 10)

tea1.course = python
tea1.course.tell_info()

12、多态

多态:同一种事物有多种形态

多态性:我们可以在不知道对象的具体类型的前提下,而直接使用对象

 class Animal:
     def speak(self):
         pass

 class Pig(Animal):
     pass

 class People(Animal):
     pass

 class Dog(Animal):
     pass

 obj1 = Pig()
 obj2 = People()
 obj3 = Dog()

 def speak(animal):
     animal.speak()

 speak(obj1)
 speak(obj2)
 speak(obj3)

我们使用的len()函数也是如此,虽然数据类型不同但是使用len()函数的方法是一样的。

如果这样使用的话就要保证子类中必须要有这个功能,而且命名要与父类一样,所以有一种方法可以规定命名:

import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def speak(self):
        pass

    @abc.abstractmethod
    def run(self):
        pass

class Pig(Animal):
    def speak(self):
        pass
    def run(self):
        pass

class People(Animal):
    def speak(self):
        pass

    def run(self):
        pass

class Dog(Animal):
    def speak(self):
        pass

    def run(self):
        pass

Pig()
People()
Dog()

但是这样只会让代码变得更加多,与Python追求的语法的极简是相违背的,所以python崇尚鸭子类型:

class Pig:
    def speak(self):
        pass
    def run(self):
        pass

class People:
    def speak(self):
        pass

    def run(self):
        pass


class Dog:
    def speak(self):
        pass

    def run(self):
        pass

就是写代码是就写成一样,他们很像,那就是一样的,用法也是一样的。

 

posted @ 2021-04-12 19:54  余鑫  阅读(241)  评论(0)    收藏  举报