Python之面对对象

时间:2025-02-12 17:36:05

面向对象

引导
  • 你现在是一家游戏公司的开发人员,现在需要你开发一款叫做<人狗大战>的游戏,你就思考呀,人狗作战,那至少需要2个角色,一个是人, 一个是狗,且人和狗都有不同的属性和技能,比如人拿棍打狗, 狗可以咬人,怎么描述这种不同的角色和他们的功能呢?

  • 首先思考:如何封装角色的属性呢?
    def Dog(name,s_type):
        data = {
            'name':name,
            's_type':s_type
        }
        return data
    def Person(name,age,sex):
        data = {
            'name':name,
            'age':age,
            'sex':sex
        }
        return data
    
  • 两个角色对象生成了,狗和人还有不同的功能呀,狗会咬人,人会打狗. 怎么实现呢,。。想到了, 可以每个功能再写一个函数,想执行哪个功能,直接调用 就可以了,对不?

def Dog(name,s_type):
    def hitPerson(d):
        print('dog %s is hitting Person'%data['name'])
    data = {
        'name':name,
        's_type':s_type,
        'hitPerson':hitPerson,
    }
    
    return data
def Person(name,age,sex):
    def hitDog(p):
        print('person %s is hitting dog'%data['name'])
    def walk(p):
        print('person %s is walking '%data['name'])
    data = {
        'name':name,
        'age':age,
        'sex':sex,
        'hitDog':hitDog,
        'walk':walk
    }
    
    return data

上面两个函数相当于造了两个模子。游戏开始,如何塑造一个具体的人或者狗的角色呢?

d1 = Dog('doudou','金毛')
d2 = Dog('xixi','哈士奇')
p1 = Person('zhangsan',20,'F')
  • 上面的功能实现的简直是完美!但是仔细玩耍一会,你就不小心干了下面这件事
d1['hitPerson'](p1)
p1['hitDog'](d1)
  • 事实上,从你写的代码上来看,这并没出错。很显然,人是不能调用狗的功能的,但在你的程序中目前没有好的办法进行限制。
  • 刚才我们用的这种编程思想其实就是简单的面向对象编程。
    • 我们其实是将游戏中所有的人和狗两种角色的共同之处封装在了两个模子中,使用模子可以创建不同的人和狗的具体角色。具体人and狗之间的交互就等着程序员结合着实际的业务场景去使用了。
    • 假如人和狗打起来了,这时候人是走路还是拿棍子打狗就由程序员或者用户自己决定了。那你的每一个决定可能都影响着你这场游戏的输赢。
  • 尽管如此,我们也只完成了这个游戏非常小的一部分。还有很多功能都没有实现。
  • 思考
    • 有没有可能 ,同一个种角色,有些属性和方法是相同的但是有些是不同的呢?
    • 比如 ,大家都打过cs吧,cs里有警察和恐怖份子,但因为都是人, 所以你写一个角色叫 person的模子, 警察和恐怖份子都有名字属性,有可以互相射击的功能,但警察不可以杀人质,*可以,这怎么实现呢?
      • 你想了想说,这个简单啊,只需要在杀人质的功能里加个判断,如果是警察,就不让杀不就ok了么。 没错, 这虽然解决了杀人质的问题,但其实你会发现,警察和*的区别还有很多,如果在每个区别处都单独做判断,那得累死。
    • 你想了想说, 那就直接写2个角色吧, 反正这么多区别, 我的哥, 不能写两个角色呀,因为他们还有很多共性 , 写两个不同的角色,就代表相同的功能也要重写了。
面向过程VS面向对象
  • 面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的思想就好比是精心设计好一条流水线,考虑周全什么时候处理什么东西。

  • 优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。

  • 缺点是:一套流水线就是用来解决一个问题,代码牵一发而动全身。

  • 应用场景:

    • 一旦完成基本很少改变的场景,著名的例子有Linux內核,git,以及Apache HTTP Server等。
  • 面向对象OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,并且一个对象包含数据和操作数据的方法。

  • 面向对象的程序设计的核心是对象,要理解对象为何物,必须把自己当成上帝(上帝式思维)。上帝眼里世间存在的万物皆为对象。
  • 形象化场景设计:

    • 面向对象的程序设计好比如来设计西游记,如来要解决的问题是把经书传给东土大唐,"如来”想了想解决这个问题需要四个人(对象):唐僧,沙和尚,猪八戒,孙悟空,每个人都有各自的特征和技能(这就是对象的概念,特征和技能分别对应对象的属性和方法)。然而这并不好玩,于是如来又安排了一群妖魔鬼怪,为了防止师徒四人在取经路上被搞死,又安排了一群神仙保驾护航,这些都是对象。然后取经开始,师徒四人与妖魔鬼怪神仙互相缠斗着直到最后取得真经。“如来”根本不会管师徒四人按照什么流程去取,只关心最后结果是否可以实现。
    • 因此面向对象的核心思想就是使用一个又一个的对象来完成某件具体是事件,且不用关心完成的具体过程!
  • 面向对象的优点:面向对象编程可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使他人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

  • 应用场景:需求经常变化的软件,如互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。

类和实例

,英文名字Class,有“类别”,“分类”,“聚类”的意思。

必须牢记类是抽象的模板用来描述具有相同属性和方法的对象的集合,比如Animal类。

实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

  • Python使用class关键字来定义类,其基本结构如下:

  • class ClassName():
        pass
    
  • 下面是一个学生类:

  • class Student():
        address = 'Beijing"
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def read(self):
            pass
        def learn(self)
        	pass
    
  • 对象的创建

    • 可以通过调用类的实例化方法(有的语言中也叫初始化方法或构造函数)来创建一个类的实例(对象)。

    • Python提供了一个def __init__(self):的实例化机制。任何一个类中,名字为__init__的方法就是类的实例化方法,具有__init__方法的类在实例化的时候,会自动调用该方法,并传递对应的参数。

    • zhangsan = Student('zhangsan',20)
      
实例变量和类变量
  • 实例变量

    • 实例变量指的是实例(对象)本身拥有的变量。Student类中__init__方法里的变量就是实例变量。

    • 通过实例名加圆点的方式调用实例变量。

    • s1 = Student('zhangsan',20)
      s1.name
      
  • 类变量

    • 定义在类中,构造方法之外的变量,称作类变量。类变量是所有实例公有的变量,每一个实例都可以访问类变量。

    • 类变量可以通过类名或者实例名加圆点的方式访问类变量,比如:

    • s1 = Student('zhangsan',20)
      s2 = Student('lisi',30)
      print(Student.address) #通过类名访问类属性
      print(s1.address,s2.address)#对象访问类属性,核实下类属性是否可以被对象共享
      
    • 思考:如何修改类变量中存储的数据?

      • 通过类名修改?
      • 通过实例名修改?
    #通过类名修改
    class Student():
        address = 'Beijing' #类属性
        def __init__(self,name,age):
            self.name = name  #实例属性
            self.age = age #实例属性
        def read(self):
            pass
        def learn(self):
        	pass
    
    s1 = Student('zhangsan',20)
    s2 = Student('lisi',30)
    #通过类名访问类属性对齐进行修改,然后通过对象名访问类属性查看修改后的效果
    print('修改前的类属性',Student.address)
    Student.address = 'shanghai' #通过类名修改类属性
    print('修改后的类属性',Student.address)
    
    #通过实例名修改
    class Student():
        address = 'Beijing' #类属性
        def __init__(self,name,age):
            self.name = name  #实例属性
            self.age = age #实例属性
        def read(self):
            pass
        def learn(self):
        	pass
    
    s1 = Student('zhangsan',20)
    s2 = Student('lisi',30)
    #通过类名访问类属性对齐进行修改,然后通过对象名访问类属性查看修改后的效果
    print('修改前的类属性',Student.address)
    s1.address = "shanghai" #通过对象名访问类属性对其进行修改
    print('修改后的类属性',s2.address)
    

    因此一定要注意:

    记住:通过对象名打点的方式只可以访问类属性,不可以修改.
        如果直接通过对象名打点的方式修改类属性是不可以的,该种方式并不是对类属性进行修改,而是给该对象新增一个和类属性同名的实例变量.
    
类的方法

Python的类中包含实例方法、静态方法和类方法三种方法。区别在于传入的参数和调用方式不同。

在类的内部,使用def关键字来定义一个方法。

实例方法
  • 类的实例方法由实例调用,至少包含一个self参数,且为第一个参数。执行实例方法时,会自动将调用该方法的实例赋值给self。

    • self代表的是类的实例,而非类本身。self不是关键字,而是Python约定成俗的命名,你完全可以取别的名字,但不建议这么做。

    • class Student():
          address = 'Beijing'             #类属性
          def __init__(self,name,age):
              self.name = name            #实例属性
              self.age = age              #实例属性
          def read(self):
              print(self)                 #self就是read方法的调用者对应的对象
              print('student can read!')
          def learn(self):
          	pass
      
      s1 = Student('zhangsan',20)
      s1.read()                           #不需要给self主动传参
      print(s1)
      

对象之间的交互:可以将一个对象作为另一个对象方法的参数进行传递。

  • 设计人狗大战游戏,让他们真正的打一架。
class Dog():
    #构造方法的作用:对象的初始化(给创建好的对象的属性进行赋值)
    def __init__(self,name,blood,agg_value):
        self.name = name
        self.blood = blood
        self.agg_value = agg_value
    def hitPerson(self,p):                     # p就是攻击的某个人物对象
        p.blood -= self.agg_value
    def showBlood(self):
        return self.blood
class Persion():
    def __init__(self,name,blood,agg_value):
        self.name = name
        self.blood = blood
        self.agg_value = agg_value
    def hitDog(self,d):                       #  被攻击者对象
        d.blood -= self.agg_value
    def showBlood(self):
        return self.blood

d1 = Dog('doudou',100,20)
d2 = Dog('xixi',100,20)
p1 = Persion('zhangsan',100,30)
p2 = Persion('wangwu',100,30)
#让d1去攻击p1
d1.hitPerson(p1)
d1.hitPerson(p1)
print(p1.showBlood())
静态方法
  • 静态方法由类调用,无默认参数。将实例方法参数中的self去掉,然后在方法定义上方加上@staticmethod,就成为静态方法。

  • 静态方法属于类,和实例无关。建议只使用类名.静态方法的调用方式。(虽然也可以使用实例名.静态方法的方式调用)

  • @staticmethod
    def staticFunc(): #静态方法
        print('i am static func')
    
类方法
  • 类方法由类调用,采用@classmethod装饰,至少传入一个cls(代指类本身,类似self)参数。

  • 执行类方法时,自动将调用该方法的类赋值给cls。建议只使用类名. 类方法的调用方式。

    (虽然也可以使用实例名.类方法的方式调用)

  • class Obj():
        def instanceFunc(self):            # 普通的实例方法
            print('i am instance func')
    
        @classmethod                       # 装饰器
        def classFunc(cls):                # 类方法,cls就是当前类
            print('i am class func')
    
    Obj.classFunc()                        # 尽量使用类名调用类方法
    
面向对象的组合用法
  • 组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

  • 思路设计:一个学生会有一部手机,学生使用手机看电影。

class CellPhone():
    def __init__(self,color,c_type):
        self.color = color
        self.c_type = c_type
    def watchMovie(self,title):
        print('watching  %s ......'%title)

class Student():
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.cellPhone = CellPhone('red','iphone')

s1 = Student('lisi',20)
s1.cellPhone.watchMovie('霸王别姬')
相关概念总结:
  1. 类:就是一个抽象的模板
  2. 对象也可以叫做实例.
  3. 创建一个对象也可以叫做实例化一个对象
属性:
  1. 类变量也可以叫做类属性.属于类所有,但是可以被所有的对象共享.所有的对象可以访问类属性,但是不可以通过对象的方式修改类属性
  2. 实例变量也叫做对象的属性.属于对象所有的,只能通过对象来调用
方法:
  1. 实例方法也可以叫做对象方法,属于对象所有的.有一个比较特殊的实例方法就是__init__可以叫做"构造方法",其作用就是用来对对象的属性进行初始化赋值
  2. 类方法,属于类所有,但是可以被所有的对象共享.最好使用类名调用类方法
  3. 静态方法,就是在类中定义的一个普通的函数.