面向对象

 

面向过程编程

1.面向过程是一种编程思路、思想,而编程思路是不依赖于具体的语言或语法的。
言外之意是即使我们不依赖于函数,也可以基于面向过程的思想编写程序
2.定义 面向过程的核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么 基于面向过程设计程序就好比在设计一条流水线,是一种机械式的思维方式 3.优点:复杂的问题流程化,进而简单化 4.缺点:可扩展性差,修改流水线的任意一个阶段,都会牵一发而动全身

面向对象

1.定义
面向对象的程序设计:核心是对象二字,对象是特征与技能的结合体
优点:解决了程序的扩展性。
缺点:设计复杂

类与对象

什么是类?
   类即类别、种类,是面向对象设计最重要的概念,
对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体

类:先定义类,后产生对象

# 一:先定义类
class Student:
    # 1、变量的定义
    stu_school = 'oldboy'

    # 空对象
    def __init__(obj, x, y, z):
        obj.stu_name = x  
        obj.stu_age = y  
        obj.stu_gender = z  
        

    # 2、功能的定义
    def tell_stu_info(stu_obj):
        print('学生信息:名字:%s 年龄:%s 性别:%s' % (
            stu_obj['stu_name'],
            stu_obj['stu_age'],
            stu_obj['stu_gender']
        ))

    def set_info(stu_obj, x, y, z):
        stu_obj['stu_name'] = x
        stu_obj['stu_age'] = y
        stu_obj['stu_gender'] = z


stu1_obj = Student('egon', 18, 'male')
stu2_obj = Student('lili', 19, 'female')
stu3_obj = Student('jack', 20, 'male')

print(stu1_obj.__dict__)
print(stu2_obj.__dict__)
print(stu3_obj.__dict__)

 

类中存放的是对象共有的数据与功能
一:类可以访问:
1、类的数据属性

print(Student.stu_school)

2、类的函数属性

print(Student.tell_stu_info)

二:但其实类中的东西是给对象用的
1、类的数据属性是共享给所有对象用的,大家访问的地址都一样

print(id(stu1_obj.stu_school))
print(id(stu2_obj.stu_school))
print(id(stu3_obj.stu_school))

2、类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同
类调用自己的函数属性必须严格按照函数的用法来

print(Student.tell_stu_info)
print(Student.set_info)
Student.tell_stu_info(stu1_obj)
Student.tell_stu_info(stu2_obj)
Student.tell_stu_info(stu3_obj)

绑定方法的特殊之处在于:谁来调用绑定方法就会将谁当做第一个参数自动传入

print(Student.tell_stu_info)
print(stu1_obj.tell_stu_info)
print(stu2_obj.tell_stu_info)
print(stu3_obj.tell_stu_info)

stu1_obj.tell_stu_info()
stu2_obj.tell_stu_info()
stu3_obj.tell_stu_info()

封装

面向对象编程有三大特性:封装、继承、多态,其中最重要的一个特性就是封装。

封装指的就是把数据与功能都整合到一起

将封装的属性进行隐藏操作
1、如何隐藏:在属性名前加__前缀,就会实现一个对外隐藏属性效果
该隐藏需要注意的问题:
I:在类外部无法直接访问双下滑线开头的属性,但知道了类名和属性名就可以拼出名字:_类名__属性,
然后就可以访问了,如Foo._A__N,
所以说这种操作并没有严格意义上地限制外部访问,仅仅只是一种语法意义上的变形。

class Foo:
    __x = 1  # _Foo__x

    def __f1(self):  # _Foo__f1
        print('from test')


print(Foo.__dict__)
print(Foo._Foo__x)
print(Foo._Foo__f1)

II:这种隐藏对外不对内,因为__开头的属性会在检查类体代码语法时统一发生变形

class Foo:
    __x = 1  # _Foo__x = 1

    def __f1(self):  # _Foo__f1
        print('from test')

    def f2(self):
        print(self.__x) # print(self._Foo__x)
        print(self.__f1) # print(self._Foo__f1)

print(Foo.__x)
print(Foo.__f1)
obj=Foo()
obj.f2()

III: 这种变形操作只在检查类体语法的时候发生一次,之后定义的__开头的属性都不会变形

class Foo:
    __x = 1  # _Foo__x = 1

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

obj=Foo('egon',18)
print(obj.__dict__)
print(obj.name,obj.age)

2、为何要隐藏?
I、隐藏数据属性"将数据隐藏起来就限制了类外部对数据的直接操作,
然后类内应该提供相应的接口来允许类外部间接地操作数据,
接口之上可以附加额外的逻辑来对数据的操作进行严格地控制:

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

    def get_name(self):
        # 通过该接口就可以间接地访问到名字属性
        print(self.__name)

    def set_name(self,val):
        if type(val) is not str:
            print('小垃圾,必须传字符串类型')
            return
        self.__name=val


obj = People('egon')
# print(obj.name) # 无法直接用名字属性
# obj.set_name('EGON')
obj.get_name()

II、隐藏函数/方法属性:目的的是为了隔离复杂度

继承

什么是继承

继承是一种创建新类的方式,
新建的类可以继承一个或多个父类(python支持多继承),
父类又可称为基类或超类,新建的类称为派生类或子类。

python中类的继承分为:单继承和多继承
class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass


查看继承  __bases__
 SubClass1.__bases__
(<class '__main__.ParentClass1'>,)

经典类与新式类

1.只有在python2中才分新式类和经典类,python3中统一都是新式类
2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
3.在python2中,先声明继承object的类,以及该类的子类,都是新式类
3.在python3中,无论是否继承object,都默认继承object,python3为新式类

继承与抽象(先抽象再继承)

 

 

 

继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

 

 

 

继承与重用性

  动物:吃、喝、拉、撒

     猫:喵喵叫(猫继承动物的功能)

     狗:汪汪叫(狗继承动物的功能)


class 动物:

    def 吃(self):
        # do something

    def 喝(self):
        # do something

    def 拉(self):
        # do something

    def 撒(self):
        # do something

# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 猫(动物):

    def 喵喵叫(self):
        print '喵喵叫'
        
# 在类后面括号中写入另外一个类名,表示当前类继承另外一个类
class 狗(动物):

    def 汪汪叫(self):
        print '喵喵叫'




#继承的代码实现
class Animal:

    def eat(self):
        print("%s 吃 " %self.name)

    def drink(self):
        print ("%s 喝 " %self.name)

    def shit(self):
        print ("%s 拉 " %self.name)

    def pee(self):
        print ("%s 撒 " %self.name)


class Cat(Animal):

    def __init__(self, name):
        self.name = name
        self.breed = ''

    def cry(self):
        print('喵喵叫')

class Dog(Animal):

    def __init__(self, name):
        self.name = name
        self.breed=''

    def cry(self):
        print('汪汪叫')

 在开发程序的过程中,如果我们定义了一个类A,然后又想新建立另外一个类B,但是类B的大部分内容与类A的相同时

我们不可能从头开始写一个类B,这就用到了类的继承的概念。

通过继承的方式新建类B,让B继承A,B会‘遗传’A的所有属性(数据属性和函数属性),实现代码重用

属性查找

class Foo:
    def f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.f1()

class Bar(Foo):
    def f1(self):
        print('Foo.f1')


b=Bar()
b.f2()

有了继承关系,对象在查找属性时,先从对象自己的__dict__中找,如果没有则去子类中找,然后再去父类中找……
class Foo:
    def __f1(self): # _Foo__f1
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.__f1() # self._Foo__f1,# 调用当前类中的f1

class Bar(Foo):
    def __f1(self): # _Bar__f1
        print('Bar.f1')

obj=Bar()
obj.f2()
父类如果不想让子类覆盖自己的方法,可以采用双下划线开头的方式将方法设置为私有的

菱形问题介绍与MRO

 

 

 

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


class B(A):
    def test(self):
        print('from B')
    pass

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

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


print(D.mro()) # 类D以及类D的对象访问属性都是参照该类的mro列表
obj = D()
obj.test()
总结:类相关的属性查找(类名.属性,该类的对象.属性),都是参照该类的mro

如果多继承是非菱形继承,经典类与新式的属性查找顺序一样:都是一个分支一个分支地找下去,然后最后找object

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

class F:
    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:
    def test(self):
        print('from D')
        
class A(B, C, D):
    def test(self):
        print('from A')

# 新式类
print(A.mro()) # A->B->E->C->F->D->object
obj = A()
obj.test()

如果多继承是菱形继承,经典类与新式类的属性查找顺序不一样:
    经典类:深度优先,会在检索第一条分支的时候就直接一条道走到黑,即会检索大脑袋(共同的类)
    新式类:广度优先,会在检索最后一条分支的时候检索大脑袋

# 在python2中,未继承object的类及其子类,都是经典类
class G: 
    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')

# 新式类
print(A.mro()) # A->B->E->C->F->D->G->object

# 经典类:A->B->E->G->C->F->D
obj = A()
obj.test()

多继承到底要不用???

要用,但是规避几点问题:

      1、继承结构尽量不要过于复杂
      2、推荐使用mixins机制:在多继承的背景下满足继承的什么"是"什么的关系

装饰器是在不修改被装饰对象源代码以及调用方式的前提下为被装饰对象添加
新功能的可调用对象property是一个装饰器,是用来绑定给对象的方法伪造成一个数据属性

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

    # 定义函数的原因1:
    # 1、从bmi的公式上看,bmi应该是触发功能计算得到的
    # 2、bmi是随着身高、体重的变化而动态变化的,不是一个固定的值说白了,每次都是需要临时计算得到的

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


obj1 = People('egon', 70, 1.83)
print(obj1.bmi())

print(obj1.bmi)

多继承:可以继承多个父类

      优点:子类可以同时遗传多个父类的属性,最大限度地重用代码
      缺点:
        1、违背人的思维习惯:继承表达的是一种什么"是"什么的关系
        2、代码可读性会变差
        3、不建议使用多继承,有可能会引发可恶的菱形问题,扩展性变差,
          如果真的涉及到一个子类不可避免地要重用多个父类的属性,应该使用

 

当然子类也可以添加自己新的属性或者在自己这里重新定义这些属性(不会影响到父类),需要注意的是,

一旦重新定义了自己的属性且与父类重名,那么调用新增的属性时,就以自己为准了。

class Riven(Hero):
    camp='Noxus'
    def attack(self,enemy): #在自己这里定义新的attack,不再使用父类的attack,且不会影响父类
        print('from riven')
    def fly(self): #在自己这里定义新的
        print('%s is flying' %self.nickname)

在子类派生的新方法中如何重用父类的功能
方式一:指名道姓调用某一个类下的函数 不依赖于继承关系

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

    def f1(self):
        print('%s say hello' %self.name)


class Teacher(OldboyPeople):
    def __init__(self,name,age,sex,level,salary):
        OldboyPeople.__init__(self,name,age,sex)

        self.level = level
        self.salary=salary

tea_obj=Teacher('egon',18,'male',10,3000)
print(tea_obj.__dict__)

方式二:super()调用父类提供给自己的方法    严格依赖继承关系
调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro,去当前类的父类中找属性

class A:
    def test(self):
        print('from A')
        super().test()

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

class C(A,B):
    pass


obj=C()
obj.test()

print(C.mro())

 

组合与重用性

软件重用的重要方式除了继承之外还有另外一种方式,即:组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

class Equip: #武器装备类
    def fire(self):
        print('release Fire skill')
class Riven: #英雄Riven的类,一个英雄需要有装备,因而需要组合Equip类
     camp='Noxus'
     def __init__(self,nickname):
        self.nickname=nickname
         self.equip=Equip() #用Equip类产生一个装备,赋值给实例的equip属性

r1=Riven('锐雯雯')
r1.equip.fire() #可以使用组合的类产生的对象所持有的方法
release Fire skill

组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同,

1.继承的方式

通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人

2.组合的方式

用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系,比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...

多态

多态指的是一类事物有多种形态

import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #动物的形态之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')

为何要有多态?

  1.增加了程序的灵活性

  2.增加了程序额可扩展性

多态会带来什么样的特性?

多态性,多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象

class Animal: # 统一所有子类的方法
    def say(self):
        print('动物基本的发声频率。。。',end=' ')

class People(Animal):
    def say(self):
        super().say()
        print('嘤嘤嘤嘤嘤嘤嘤')

class Dog(Animal):
    def say(self):
        super().say()
        print('汪汪汪')

class Pig(Animal):
    def say(self):
        super().say()
        print('哼哼哼')


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


obj1.say()
obj2.say()
obj3.say()

 

文件有多种形态:文本文件,可执行文件

import abc
class File(metaclass=abc.ABCMeta): #同一类事物:文件
    @abc.abstractmethod
    def click(self):
        pass

class Text(File): #文件的形态之一:文本文件
    def click(self):
        print('open file')

class ExeFile(File): #文件的形态之二:可执行文件
    def click(self):
        print('execute file')

鸭子类型

#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
    def read(self):
        pass

    def write(self):
        pass

class DiskFile:
    def read(self):
        pass
    def write(self):
        pass

绑定方法与非绑定方法

一:绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):

    1. 绑定到类的方法:用classmethod装饰器装饰的方法。

                为类量身定制

                类.boud_method(),自动将类当作第一个参数传入

              (其实对象也可调用,但仍将类当作第一个参数传入)

    2. 绑定到对象的方法:没有被任何装饰器装饰的方法。

               为对象量身定制

               对象.boud_method(),自动将对象当作第一个参数传入

             (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)

二:非绑定方法:用staticmethod装饰器装饰的方法

        1. 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已

    注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,
对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说

绑定方法

import settings
class MySQL:
    def __init__(self,host,port):
        self.host=host
        self.port=port

    @classmethod
    def from_conf(cls):
        print(cls)
        return cls(settings.HOST,settings.PORT)

print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
conn=MySQL.from_conf()

conn.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类

非绑定方法

import hashlib
import time
class MySQL:
    def __init__(self,host,port):
        self.id=self.create_id()
        self.host=host
        self.port=port
    @staticmethod
    def create_id(): #就是一个普通工具
        m=hashlib.md5(str(time.time()).encode('utf-8'))
        return m.hexdigest()


print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数
conn=MySQL('127.0.0.1',3306)
print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数

classmethod与staticmethod的区别

import settings
class MySQL:
    def __init__(self,host,port):
        self.host=host
        self.port=port

    @staticmethod
    def from_conf():
        return MySQL(settings.HOST,settings.PORT)

    # @classmethod #哪个类来调用,就将哪个类当做第一个参数传入
    # def from_conf(cls):
    #     return cls(settings.HOST,settings.PORT)

    def __str__(self):
        return '就不告诉你'

class Mariadb(MySQL):
    def __str__(self):
        return '<%s:%s>' %(self.host,self.port)


m=Mariadb.from_conf()
print(m) #我们的意图是想触发Mariadb.__str__,但是结果触发了MySQL.__str__的执行,打印就不告诉你:

mariadb是mysql

 

posted @ 2020-04-26 08:15  zhw_sylvia  阅读(124)  评论(0)    收藏  举报