说道面向对象,我们在之前的文章中有提过,同时我们也说过编程语言中的三大编程风格,也就是:面向过程,函数式编程,面向对象;那么我们现在就来看一下什么是面向对象.还是一样,我们先来看一个案例
def eat(food): print("在吃%s"%food) Animal={ "eat":eat } People={ "eat":eat } Animal["eat"]("肉") People["eat"]("肉")
从以上例子中,我们定义了两个字典,Animal和People,它们都有eat的属性,但是我们发现,虽然它们都与eat的属性,可是它们去执行的时候是不一样的,对eat方法来说,它并没有被很好的保护,没有进行针对性.那么面向对象要做的事情就是去解决这样的问题,当然还有很多,接下来我们慢慢分析
我们从编程语言的发展来看,编程从开始是无组织无结构的,从简单的控制流程来实现相应的功能,由于代码的非结构化,有的时候我们有的功能在很多个地方都需要用到,这个时候我们就需要不断的写重复代码了,很多重复代码放在一个程序中,看起来就将会很臃肿,如果有一天我们这些功能改变需求了,我们这个时候就需要一个地方一个地方的去找了,这样的去维护一个程序,工作量就可想而知了,后来人们想到,对于这些重复的代码,我们可以把它们抽取出来,组织到一起定义成一个函数,这时候代码看着就比较有层次了,逻辑也清晰明了,我们要修改的时候,只有在这个函数修改就可以了,但是后来人们又考虑到一个问题,比如一条狗,我只想让它有看家的功能,一只猫,我就是让它抓老鼠的,但是,怎么才能让狗不会去执行到猫的方法呢?这时候人们又想到,可以吧这些特定的功能特定的属性封装起来,这个时候,面向对象就产生了.那么面向对象在编程中又是怎么涉及来的呢?
# 我们要设计一个面向对象,我们就从最基本的函数来进行设计一个动物和人 def init(self,name,gender): ''' 初始化属性 :param self: :param name: :param gender: :return: ''' self["name"] = name self["gender"] = gender return self def speak(self): ''' 说话是人具备的 :param self: :return: ''' print("【%s】在大声说话"%self["name"]) def call(self): ''' 叫是狗具备的 :param self: :return: ''' print("【%s】正在悲惨的汪汪叫"%self["name"]) Dog={ "init":init, "call":call # 狗有叫的属性 } People={ "init":init, "speak":speak # 人有说话的属性 } # 创建一条狗,让它悲惨的叫 dog = {} Dog["init"](dog,"阿黄","female") Dog["call"](dog) # 创建一个人,让他疯狂的大声说话 people = {} People["init"](people,"阿Q","man") People["speak"](people) # 注:整个实现下来,我们发现People是调不到Dog里面的方法的,Dog也一样,这就是面向对象的设计,也就是说,不能说通过def来定义就不是面向对象,只要我们的设计师符合面向对象思想的,那就是面向对象
# 通过专门语法class来设计面向对象 class Dog: def __init__(self,name,gender): ''' 初始化 :param name: :param gender: ''' self.name = name self.gender = gender def call(self): print("【%s】正在悲惨的汪汪叫" % self.name) class People: def __init__(self,name,gender): ''' 初始化 :param name: :param gender: ''' self.name = name self.gender = gender def speak(self): print("【%s】在大声说话" % self.name) # 创建一条狗 dog = Dog('阿黄',"female") dog.call() # 创建一个人 people = People("阿Q","man") people.speak()
面向对象的设计我们分析完了,接下来我们看两个概念,一个是类,一个是对象;类是一种数据结构,就好比一个模型,该模型拥有一些特征和功能,从而用它来表述一类事物,这是一种抽象的概念,而对象,它是这类事物的一个实例,是一个具体的存在
类,是一类事物,是事物就有属性,类的属性分为:
1,数据属性:也就是变量
2,函数属性:也就是函数
class People: ''' 只要是人都有两只眼睛,两只手,两只脚 ^-^ 数据属性 ''' eyes = 2 foots = 2 hands = 2 def __init__(self,name,gender): self.name = name self.gender = gender # 函数属性 def shopping(self,supermarket): print("【%s】正在【%s】购物"%(self.name,supermarket)) # 查看一个类有哪些属性 print(People.__dict__) # 如果要对属性进行操作,通过点 . 来实现,比如 print(People.eyes) p = People("阿Q","man") People.shopping(p,"天价超市")
对象,就只有数据属性了
# 静态属性 类方法 静态方法 class People: ''' 只要是人都有两只眼睛,两只手,两只脚 ^-^ 数据属性 ''' eyes = 2 foots = 2 hands = 2 def __init__(self,name,gender): self.name = name self.gender = gender # 函数属性 def shopping(self,supermarket): print("【%s】正在【%s】购物"%(self.name,supermarket)) # 静态属性 @property def get_info(self): print("【%s】是【%s】"%(self.name,self.gender)) # 类方法 只能各类使用,是能访问实例变量 @classmethod def describe(cls): print("【%s】只眼睛【%s】只脚 【%s】只胳膊 是人的共同属性" % (cls.eyes,cls.foots,cls.hands)) # 静态方法 @staticmethod def walking(): print("走啊走啊走") p = People("阿Q","man") p.shopping("天堂超市") # 调用静态属性不需要括号 像数据属性那样调用就可以了 p.get_info # 调用类方法 People.describe() # 静态方法 类和实例都可以调用 People.walking() p.walking()
组合,就是把一些属性不相同,但是它们是有关联的,有这些属性来组成一个大的类,直接通过一个案例来展示:
# 模仿一个选课系统 # 将学校组合的选课系统里边 class School: def __init__(self,name): self.name = name class CourseSelectionSystem: def __init__(self,school,course): self.school = school self.course = course def select_info(self): print("您在【%s】选的课如下:\n%s"%(self.school.name,self.course)) def select_course(): school = input("请输入学校名 >> ") school = School(school) courses = [] while True: course = input("请输入你想选择的课程,确定之后,输入ok结束 >> ") if course == "ok": c = CourseSelectionSystem(school, courses) c.select_info() break courses.append(course) select_course()
面向对象的三大特性之继承
# 在python中,继承有单继承和多继承,比如: class Earthworm: def __init__(self): print("蚯蚓") class SubEarthworm(Earthworm): def single(self): print("子蚯蚓,是单继承") class MaleDog: def show_male_info(self): print("公狗") class FemaleDog: def show_female_info(self): print("母狗") class SubDog(MaleDog,FemaleDog): def more(self): print("小狗,多继承") se = SubEarthworm() se.single() sd = SubDog() sd.show_male_info() sd.show_female_info() sd.more() # 注 子类继承了父类的属性,当子类的属性名和父类的属性名重名的时候,子类并没有覆盖父类,而是在调用的时候,调用的是子类的
我们为什么需要继承呢?也就是当类之间有相同的属性,而这些属性都是相同的,这个时候我们通过继承,就可以避免了代码重复,但是如果我们在定义基类之后,并对这些属性进行了定义,那么这个时候就会产生了一个严重的问题就是子类和父类之间出现了强耦合,这种现象我们往往是不允许的,比如说,我们定义了一个动物基类,所有的动物都有吃的功能,但是,狗吃的是骨头,而猫吃的是鱼,所有吃的这个功能一旦在基类定义了执行类型之后,可以说子类基本都用不了,所以往往我们定义的基类都是一个接口类,也就是说基类只定义了吃的功能,而没有进行操作它,让子类去实现就可以了,这种继承我们可以叫做"接口继承",也可以叫做归一化继承
# 接口继承 我们需要引入模块 abc import abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def eat(self): pass @abc.abstractmethod def call(self): pass class Dog(Animal): def __init__(self,name): self.name = name def look_home(self): print("%s站在门口当保安"%self.name) def eat(self): print("%s站在门口啃骨头"%self.name) def call(self): print("%s对着门口汪汪叫"%self.name) class Cat(Animal): def __init__(self,name): self.name = name def sleep(self): print("%s站在门口睡大觉" % self.name) def eat(self): print("%s站在门口找鱼刺" % self.name) def call(self): print("%s对着门口饿的喵喵叫" % self.name) # 在这里如果我们没有实现父类定义的抽象方法的话,就会报这样的错误 Can't instantiate abstract class Dog with abstract methods call, eat d = Dog("阿黄") d.look_home() d.call() c = Cat("阿猫") c.sleep() c.call()
多态,说白了就是不同的实例对象调用了相同的方法,只是他们的执行逻辑不一样而已,它的实质就是继承的实现细节,是一种动态的绑定,在运行的时候确定执行什么样的逻辑,比如:
class Employee: def __init__(self,name,working_years): self.name = name self.working_years = working_years def get_bonus(self): if self.working_years < 1: print("【%s】 今年发了 【1000 块钱】 的年终奖"%(self.name)) elif self.working_years >= 1 and self.working_years <= 2: print("【%s】 今年发了 【10000 块钱】 的年终奖"%(self.name)) else: print("【%s】 今年发了 【20000 块钱】 的年终奖"%(self.name)) class XiaoM(Employee): pass class XiaoW(Employee): pass class XiaoH(Employee): pass xm = XiaoM("小明",0.8) xw = XiaoW("小王",3) xh = XiaoH("小华",2) # 我们在父类中定义了获取年终奖的这个方法,然后分别定义了三个子类,让这三个三个子类去掉用这个方法,这三个子类是不一样的,只有当他们去调用的时候,才知道要执行什么样的逻辑,从而这也体现了一种动态绑定,这就是多态的体现 xm.get_bonus() xw.get_bonus() xh.get_bonus()
封装, 从本质上来讲,就是明确的区分内外部,比如类就是一种封装,就像一个麻袋一样把东西装起来;还有就是在类中定义了私有属性,目的只是在内部使用,但是python没有特定这样做,只是做了一些约定,表示这是我私有的,外部不应该来访问,比如通过单下划线和双下划线来表示,如:
class People: _country = "中国人" def __init__(self,name): self._name = name def _is(self): print("【%s】 是一名 【%s】"%(self._name,self._country)) def __is(self): print("【%s】 是一名真正的 【%s】 而不是岛国人" % (self._name, self._country)) print(People._country) # 输出结果是: 中国人 p = People("小王") p._is() # 输出结果是:【小王】 是一名 【中国人】 # p.__is() # 这个时候,我们这样调用就会报错:AttributeError: 'People' object has no attribute '__is' 给人的感觉就是真正的实现了私有了,原来python并不只是约定,那么来看这一下,People具有哪些属性 print(People.__dict__) # 我们发现__is在People属性变成了_People__is,那么这个时候我试着去调用这个_People__is,发现调用成功 p._People__is() # 输出结果是:【小王】 是一名真正的 【中国人】 而不是岛国人 # 注python只是约定,而并没有做限制,而双下划线也没进行真正的私有操作,只是将其变成了_类名__函数名这样了
还有另外一个层面的封装就是,明确区分了内外,内部的实现逻辑,外部是无法知晓的,我们只是给外部提供了接口;python的这种区分内外的封装,只是让类的实现者可以修改封装内的东西而不影响外部调用者,同时,也给外部调用者一个提示,哪里可以动哪里不可以动
浙公网安备 33010602011771号