105、面向对象

面向对象

1、面向过程和面向对象

1.1 编程范式

编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程 , 一个程序是程序员为了得到一个任务结果而编写的一组指令的集合
实现一个任务的方式有很多种不同的方式, 对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为
编程范式
。 不同的编程范式本质上代表对各种类型的任务采取的不同的解决问题的思路, 大多数语言只支持一种编程范式,当然也有些语言可以同时支持多种编程范式。 两种最重要的编程范式分别是面向过程编程和面向对象编程。

1.2 面向过程编程(Procedural Programming)

就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题,那如果你改了这个变量,那这个子过程你也要修改,假如又有一个其它子程序依赖这个子过程 , 那就会发生一连串的影响,随着程序越来越大, 这种编程方式的维护难度会越来越高, 如果你只是写一些简单的脚本,去做一些一次性任务,用面向过程的方式是极好的,但如果你要处理的任务是复杂的,且需要不断迭代和维护 的, 那还是用面向对象最方便了。

1.3 面向对象编程

OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容

Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法

**Object 对象 **
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同

Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法

Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承

Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。

1.4类的实例化

class Dog(object):
    def __init__(self,name,dog_type):
        self.name = name
        self.type = dog_type
    def sayhi(self):
        print("hello,I am a dog, my name is ",self.name)
 
d = Dog('LiChuang',"京巴")
d.sayhi()

**
其实self,就是实例本身,实例化时python会自动把这个实例本身通过self参数传进去。**

1.5 面向对象的特性

class Dog(object):
    country = 'CH'#类变量、公有属性
    def __init__(self,name,type,age):#类
        self.name = name#实例变量
        self.type = type#实例变量
        self.__age = age#私有变量,封装
    def get__age(self):#打印出私有属性的方法,从内部调用
        return self.__age
    def walk(self):
        print('%s is a dog' %self.name)
    def eat(self,food):
        print('%s is eating %s' %(self.name,food))
d = Dog('Alex','jing ba','23')#生成对象
d.country = 'JP'

类变量作用
实例化
作为默认共有属性,用于全局修改,或增加 新属性

实例变量(成员属性)
每个实例存在自己内存空间里的属性
实例本身也只有数据属性,公有属性和函数都是调用的类的

公有属性 == 类变量

私有属性
__age 代表私有属性,只能在实例内部各个函数中调用
隐藏一些功能的实现细节,只给外部暴露调用接口

2、继承

通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。

要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
直接调用父类方法
继承父类方法并重构父类方法,现重构,在重构的方法里手动调用父类方法
可以定义子类自己的方法
析构方法 del

class Course(object):#object是 python2中用的,3里面已经默认继承了
    course_name = "Python 自动化"
    period = "7m"
    outline = "sdfsfsdfsfsdf"
    test = 321
    print("in course")

class SchoolMember(object):
    members = 0
    test = 123
    print("in Schoolmember")
    def __init__(self, name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
        SchoolMember.members +=1
        print("初始化了一个新学校成员",self.name)
    def tell(self):
        info = '''
        -----info of %s -------
        name: %s
        age : %s
        sex : %s
        '''%(self.name,self.name,self.age,self.sex)
        print(info)
    def __del__(self):#析构方法
        print("%s 被开除了"% self.name)
        SchoolMember.members -=1

class Teacher(SchoolMember):
    def __init__(self,name,age,sex,salary):
        SchoolMember.__init__(self,name,age,sex )#继承
        #self.name = name #t.name =name
        self.salary = salary
    def teaching(self,course):
        print("%s is teaching %s"%(self.name,course))

class Student(SchoolMember,Course):
    def __init__(self,name,age,sex,grade,teacher ):
        SchoolMember.__init__(self,name,age,sex )
        #self.name = name #t.name =name
        self.grade = grade
        self.my_teacher =  teacher

    def pay_tuition(self,amount):
        self.paid_tuition = amount
        print("stduent %s has paid tution amount %s" %(self.name,amount))

t = Teacher("Alex",22,"F",3000)
s = Student("Liuhao",24,"M","pys16", t)
# s2 = Student("YanShuai",46,"F","pys26")
# s4 = Student("NiNing",32,"F","pys26")
print("my teacher",s.my_teacher.name)

s.my_new_teacher = t

#del s4
# t.tell()
# s.tell()
# s2.tell()
# t.teaching("python")
# s.pay_tuition(11000)
# print(SchoolMember.members)
# print(s.course_name,s.outline)
# print("test:",s.test)

2.1 继承顺序

Python 中可以继承多个类,java 和 C 只能继承一个类,如果 Python 继承了多个类,那么其寻找方法有两种:深度优先(经典类)、广度优先(新式类)
如果当前类或其父类继承了 object 那么他就是新式类,否则就是经典类

2.2 组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

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() #可以使用组合的类产生的对象所持有的方法

2.3 子类调用父类的方法

方法一:父类名.父类方法()

class Vehicle: #定义交通工具类
     Country='China'
     def __init__(self,name,speed,load,power):
         self.name=name
         self.speed=speed
         self.load=load
         self.power=power

     def run(self):
         print('开动啦...')

class Subway(Vehicle): #地铁
    def __init__(self,name,speed,load,power,line):
        Vehicle.__init__(self,name,speed,load,power)
        self.line=line

    def run(self):
        print('地铁%s号线欢迎您' %self.line)
        Vehicle.run(self)#调用

line13=Subway('中国地铁','180m/s','1000人/箱','电',13)
line13.run()

方法二:super()

class Vehicle: #定义交通工具类
     Country='China'
     def __init__(self,name,speed,load,power):
         self.name=name
         self.speed=speed
         self.load=load
         self.power=power
     def run(self):
         print('开动啦...')

class Subway(Vehicle): #地铁
    def __init__(self,name,speed,load,power,line):
        #super(Subway,self) 就相当于实例本身 在python3中super()等同于super(Subway,self)
        super().__init__(name,speed,load,power)
        self.line=line
    def run(self):
        print('地铁%s号线欢迎您' %self.line)
        super(Subway,self).run()

line13=Subway('中国地铁','180m/s','1000人/箱','电',13)
line13.run()

注意:使用super调用的所有属性,都是从MRO列表当前的位置往后找,千万不要通过看代码去找继承关系,一定要看MRO列表

2.4 抽象类

抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类是从一堆中抽取相同的内容而来的,内容包括数据属性和函数属性。

import abc
class AllFile(metaclass=abc.ABCMeta): #抽象类
    def test(self):
        print('testing')
    @abc.abstractmethod
    def read(self):
        pass
    @abc.abstractmethod
    def write(self):
        pass

class Text(AllFile):#继承抽象类
    def read(self):
        print('text read')
    def write(self):
        pass

t=Text()
t.test()#通过继承调用抽象类函数
a=AllFile()
a.test()#报错无法生成对象

3、多态与多态性

多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。

在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

多态性分为静态多态性和动态多态性

动态多态性的作用

  • 增加了程序的灵活性,以不变应万变,不论对象千变万化,使用者都是同一种形式去调用
  • 增加了程序额可扩展性,通过继承父类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

4、封装

封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的或者对象操作,对不可信的进行信息隐藏,或者留下少量接口(函数)供外部访问。

在python中用双下划线的方式实现隐藏属性(设置成私有的)

class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

python并不会真的阻止你访问私有的属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import *时不能被导入,但是你from module import _private_module依然是可以导入的

其实很多时候你去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home,sys._clear_type_cache),这些都是私有的,原则上是供内部调用的,作为外部的你,一意孤行也是可以用的,只不过显得稍微傻逼一点点

4.1 特性property

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值,将一个类的函数定义成特性

import math
class Circle:
    def __init__(self,radius): #圆的半径radius
        self.radius=radius

    @property
    def area(self):
        return math.pi * self.radius**2 #计算面积

c=Circle(10)
print(c.radius)
print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值

输出结果:
10
314.1592653589793

此时的特性arear不能被赋值

4.2 静态方法staticmethod

是一种普通函数,位于类定义的命名空间中,不会对任何实例类型进行操作,python为我们内置了函数staticmethod来把类中的函数定义成静态方法

编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时就需要静态方法

class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now(): #用Date.now()的形式去产生实例,该实例用的是当前时间
        t=time.localtime() #获取结构化的时间格式
        return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并且返回
    @staticmethod
    def tomorrow():#用Date.tomorrow()的形式去产生实例,该实例用的是明天的时间
        t=time.localtime(time.time()+86400)
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

a=Date('1987',11,27) #自己定义时间
b=Date.now() #采用当前时间
c=Date.tomorrow() #采用明天的时间

print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)

4.3 类方法classmethod

类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,python为我们内置了函数classmethod来把类中的函数定义成类方法

import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    # @staticmethod
    # def now():
    #     t=time.localtime()
    #     return Date(t.tm_year,t.tm_mon,t.tm_mday)

    @classmethod #改成类方法
    def now(cls):
        t=time.localtime()
        return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪个类来调用,即用哪个类cls来实例化

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)

e=EuroDate.now()
print(e) #我们的意图是想触发EuroDate.__str__,此时e就是由EuroDate产生的,所以会如我们所愿
'''
输出结果:
year:2017 month:3 day:3

5、类的特殊成员方法

5.1 isinstance(obj,cls)和issubclass(sub,super)

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

class Foo(object):
    pass
    
obj = Foo()
isinstance(obj, Foo)

issubclass(sub, super)检查sub类是否是 super 类的派生类

class Foo(object):
    pass
class Bar(Foo):
    pass

issubclass(Bar, Foo)

5.2 反射

反射是程序可以访问、检测和修改它本身状态或行为的一种能力(自省)
作用:

  • 实现可插拔机制
  • 动态导入模块(基于反射当前模块成员)
class Foo(object):
     def __init__(self):
        self.name = 'wupeiqi'
    def func(self):
        return 'func'

obj = Foo()

# #### 检查是否含有成员 ####
hasattr(obj, 'name')
hasattr(obj, 'func')
 
# #### 获取成员 ####
getattr(obj, 'name')
getattr(obj, 'func')
 
# #### 设置成员 ####
setattr(obj, 'age', 18)
setattr(obj, 'show', lambda num: num + 1)
 
# #### 删除成员 ####
delattr(obj, 'name')
delattr(obj, 'func')

5.3 描述符__get__,__set__,__delete__

__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发
静态属性property本质就是实现了get,set,delete三种方法

原文地址:https://www.cnblogs.com/workhorse/p/6495807.html