面向对象—基础、名称空间、三大特性

一、面向对象的简介

 

面向对象

  • 初识

    • 一种新的编程思路

      • 面向过程开发:想要一个结果 写代码 实现计算结果

      • 面向对象开发:有哪些角色 角色的属性和技能之间是如何交互的

  • 什么时候用?

    复杂的 拥有开放式结局的程序 比较适合使用面向对象开发

    • 游戏

    • 购物

  • 例子:人狗大战


    alex = {
       'name': 'alex',
       'sex': '不详',
       'job': '搓澡工',
       'level': 0,
       'hp' : 250,
       'weapon':'搓澡巾',
       'ad' : 1
    }

    小白 = {
       'name':'小白',
       'kind':'泰迪',
       'hp':5000,
       'ad':249
    }

    # 1.你怎么保证所有的玩家初始化的时候都拥有相同的属性
    # 2.每来一个新的玩家,我们都自己手动的创建一个字典,然后向字典中填值
    # 3.人物和狗的技能如何去写

    # 建立人物的模子:
    def Person(name,sex,job,hp,weapon,ad,level=0):   # 人模子
       def 搓(dog):
           dog['hp'] -= dic['ad']
           print('%s 攻击了 %s,%s掉了%s点血' % (dic['name'], dog['name'], dog['name'], dic['ad']))
       dic = {
       'name': name,
       'sex': sex,
       'job': job,
       'level': level,
       'hp' :hp,
       'weapon':weapon,
       'ad' : ad,
       'action':
      }
       return dic


    # 建立狗的模子:
    def Dog(name,kind,hp,ad):
       def 舔(person):  # 函数不是一个公用的函数 是一个有归属感的函数
           person['hp'] -= dic['ad']
           print('%s 舔了 %s,%s掉了%s点血' % (dic['name'], person['name'], person['name'], dic['ad']))
       dic = {
       'name': name,
       'kind': kind,
       'hp': hp,
       'ad': ad,
       'action':
      }
       return dic

    alex = Person('alex','不详','搓澡工',250,'搓澡巾',1)
    wusir = Person('wusir','male','法师',500,'打狗棍',1000)
    小白 = Dog('小白','泰迪',5000,249)
    小金 = Dog('小金','柯基',10000,499)

    小白['action'](alex)   # 小白 舔了 alex,alex掉了249点血
    alex['action'](小白)   # alex 攻击了 小白,小白掉了1点血

    以上的程序可以用面向对象的方法解决:


    class Person: # 类名
    def __init__(self,name,sex):
    # 必须叫 __init__这个名字,不能改变的,所有的在一个具体的人物出现之后拥有的属性都可以写在这里
      self.name = 'alex'
      self.sex = 'man'

    alex = Person() # alex 就是对象 alex = Person()的过程 是通过类获取一个对象的过程 — 实例化
    # 类名()会自动调用类中的__init__方法


    class Person:       # 类名
       def __init__(self,name,sex,job,hp,weapon,ad):
           # 必须叫__init__这个名字,不能改变的,所有的在一个具体的人物出现之后拥有的属性
           self.name = name
           self.sex = sex
           self.job = job
           self.level = 0
           self.hp = hp
           self.weapon = weapon
           self.ad = ad

    alex = Person('alex','不详','搓澡工',260,'搓澡巾',1)    
    # alex 就是对象 alex = Person()的过程 是通过类获取一个对象的过程 - 实例化
    print(alex,alex.__dict__)
    # <__main__.Person object at 0x00000224364739B0>
    # {'name': 'alex', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 260, 'weapon': '搓澡巾', 'ad': 1}

    wusir = Person('wusir','male','法师',500,'打狗棍',1000)
    # print(wusir,wusir.__dict__)
    print(alex.name)   # print(alex.__dict__['name']) 属性的查看
    alex.name = 'alexsb'        # 属性的修改
    print(alex.name) # alexsb
    alex.money = 1000000        # 属性的增加
    print(alex.money) # 1000000
    print(alex.__dict__)
    # {'name': 'alex', 'sex': '不详', 'job': '搓澡工', 'level': 0, 'hp': 260, 'weapon': '搓澡巾', 'ad': 1, 'money': 1000000}
    del alex.money               # 属性的删除
    print(alex.__dict__)
  • 类和对象之间的关系?

    • 类 是一个大范围 是一个模子 它约束了事物有哪些属性 但是不能约束具体的值

    • 对象 是一个具体的内容 是模子的产物 它遵循了类的约束 同时给属性附上具体的值

    • Person是一个类 alex,wusir是这个类的对象

    • 类有一个空间,存储的是定义在class中的所有名字

    • 每一个对象又拥有自己的空间,通过对象名.__dict__就可以查看这个对象的属性和值

    • 修改列表\字典中的某个值,或者面向对象的某个属性 都不会影响这个对象\字典\列表所在的内存空间

  • 实例化所经历的步骤

    1. 实例 = 类名() 类名()之后的第一件事:开辟一块空间

    2. 首先开辟空间,调用init方法,把开辟的空间地址传递给self参数,调用__init__把空间的内存地址作为self参数传递到函数内部

    3. init方法中一般完成:把属性的值存储在self的空间里 — 对象的初始化,所有的这一对象需要使用的属性都要和self关联起来

    4. self这个地址会作为返回值,返回给“实例”,执行完__init__中的逻辑后,self变量会自动的被返回到调用处(发生实例化的地方)

  • 实例变量:self.名字

 

二、面向对象中类的成员和命名空间

  1. 命名空间

    • 在类的命名空间里:静态变量、绑定方法

    • 在对象的命名空间里:类指针、对象的属性(实例变量)

  2. 调用的习惯

    • 类名.静态变量

    • 对象.静态变量(对象调用静态变量的时候,不能对变量进行赋值操作 )

    • 绑定方法

      • 对象.绑定方法() # ==> 类名.绑定方法(对象)

      • 对象.实例变量

      • 对象

    • 对象 = 类名()

      怎么用

      • 类能做什么用:

        • 1.实例化对象

        • 2.操作静态变量

      • 什么时候是对类中的变量赋值,或者去使用类中的变量

        • 类名.名字 = ‘值’

        • print(类名.名字 )

        • print(对象名.名字 ) # 如果对象本身没有这个名字

      • 什么时候是对对象中的变量赋值

        • 对象.名字 的时候

        • self.名字 的时候

 

# 例题1
class A:
   Country = '中国'     # 静态变量/静态属性 存储在类的命名空间里的
   def __init__(self,name,age):  # 绑定方法 存储在类的命名空间里的
       self.name = name
       self.age = age
   def func1(self):
       print(self)
   def func2(self):pass
   def func3(self):pass
   def func4(self):pass
   def func5(self):pass

a = A('alex',83)
print(a.name)     # alex
print(a.Country)  # 中国(先在a的属性的小空间找Country,没有就通过类指针在A类里找Country)

print(A.Country)   # 中国
a.func1() # == A.func1(a)  
# <__main__.A object at 0x000002171C4F87F0>

# 可以看出,a实例化开辟自己的空间,调用init方法在自己的空间建立name,age变量,所以调用a.name是用自己的,而调用a.Country时,自己内部没有,就用A类的


# 例题2
class A:
   Country = '中国'     # 静态变量/静态属性 存储在类的命名空间里的
   def __init__(self,name,age,country):  # 绑定方法 存储在类的命名空间里的
       self.name = name
       self.age = age
       self.Country = country
   def func1(self):
       print(self)
   def func2(self):pass
   def func3(self):pass
   def func4(self):pass
   def func5(self):pass

a = A('alex',83,'印度')
print(a.name)    # alex
print(a.Country) # 印度
print(A.Country) # 中国

# 可以看出,a的空间里有name,Country,都用自己的


# 例题3
class A:
   Country = '中国'     # 静态变量/静态属性 存储在类的命名空间里的
   def __init__(self,name,age,country):  # 绑定方法 存储在类的命名空间里的
       self.name = name
       self.age = age
       self.country = country
   def func1(self):
       print(self)
   def func2(self):pass
   def func3(self):pass
   def func4(self):pass
   def func5(self):pass

a = A('alex',83,'印度')
b = A('wusir',74,'泰国人')
a.Country = '日本人'
print(a.Country) # 日本
print(b.Country) # 泰国
print(A.Country) # 中国

# a本来自己的空间中有Country = '印度',但是a.Country = '日本人',就在a空间改变了Country的属性


# 例题4
class A:
   Country = '中国'     # 静态变量/静态属性 存储在类的命名空间里的
   def __init__(self,name,age,country):  # 绑定方法 存储在类的命名空间里的
       self.name = name
       self.age = age   # 注意,此处没有匹配country的参数
   def func1(self):
       print(self)
   def func2(self):pass
   def func3(self):pass
   def func4(self):pass
   def func5(self):pass

a = A('alex',83,'印度')
b = A('wusir',74,'泰国人')
A.Country = '日本人'
print(a.Country)
print(b.Country)
print(A.Country)

# init的方法中没有Country的属性,所以实例化a,b的时候,调用init方法没有在自己的内部建立Country的属性,所以a.Country和b.Country都是用的A中的Country,就都是日本人
  • 由此可得出静态变量的作用

    • 如果一个变量,是所有的对象共享的值,那么这个变量应该被定义成静态变量

    • 所有和静态变量相关的增删改查都应该使用类名来处理;

      而不应该使用对象名直接修改静态变量

  1. 组合

    • 一个类的对象是另一个类对象的属性

    • 两个类之间 有 什么有什么的关系:班级有学生 学生有班级 图书有作者.....

 

三、面向对象的三大特性——继承、封装、多态

 

(一)、继承

  1. 目的:解决代码的重复性

  2. 继承中遇到的两种情况

    • 当子类和父类的方法重名的时候,我们只使用子类的方法,而不会去调用父类的方法了


      class Animal:
         def __init__(self,name):
             self.name = name
         def eat(self):
             print('%s is eating'%self.name)
      class Cat(Animal):
         def eat(self):
             print('%s吃猫粮'%self.name)
      小白 = Cat('小白')
      小白.eat()        
      # 小白吃猫粮(因为在Cat类型中有eat属性,就不调用父类的)        
    • 子类想要调用父类的方法的同时还想执行自己的同名方法

      在子类的方法中调用父类的方法 :父类名.方法名(self)


      class Animal:
         def __init__(self,name,food):
             self.name = name
             self.food = food
             self.blood = 100
         def eat(self):
             print('%s is eating %s'%(self.name,self.food))
      class Cat(Animal):
         def eat(self):
             self.blood += 100
             Animal.eat(self)
             print(f'{self.name}此时的体力为{self.blood}')
      小白 = Cat('小白','猫粮')
      小白.eat()
      # 小白 is eating 猫粮
      # 小白此时的体力为200
      print(小白.__dict__)
      # {'name': '小白', 'food': '猫粮', 'blood': 200}
    • 总结:

      • 继承语法: class 子类名(父类名):pass

      • 父类和子类方法的选择:

        • 子类的对象,如果去调用方法

          永远先调用自己的

          1. 如果自己有 用自己的

          2. 自己没有 用父类的

          3. 如果自己有 还想用父类的:直接在子类方法中调父类的方法 父类名.方法名(self)

  3. 多继承

    一个子类有多个父类

    单继承

    • 调子类的 : 子类自己有的时候

    • 调父类的 : 子类自己没有的时候

    • 调子类和父类的 :子类父类都有,在子类中调用父类的

    多继承

    • 一个类有多个父类,在调用父类方法的时候,按照继承顺序,先继承的就先寻找

    • 新式类和经典类

      python3 所有的类都继承object类,都是新式类

      在py2中 不继承object的类都是经典类

      继承object就是新式类

      在单继承方面(无论新式类,还是经典类,都是一样的)

      在多继承的情况中:

      • 对于新式类,在走到一个点,下一个点既可以从深度走,也可以从广度走,总是先走广度,广度优先

      • 在经典类中,都是深度优先,总是在一条路走不通之后再换一条路,走过的点不会再走了

      • 广度优先 C3算法

      总之:

      • 经典类 — 深度优先 新式类 — 广度优先

      • 深度优先会看

      • 广度预先遵循C3算法,要会用mro,会查看顺序

      • 经典没有mro,但新式类有

    • 通过继承实现的类的开发规范(工作中)

      • 1.不用模块的:


        class 父类
        def 子类必须实现的方法名(self,参数们):
          raise NotImplementedError('提示信息')
               
        class 子类(父类):
           def 父类要求实现的方法(self,参数们):
               print('''code''')
      • 2.用模块的


        from abc import ABCMeta,abstractmethod
        class 父类(metaclass = ABCMeta):
            @abstractmethod
        def 子类必须实现的方法名(self,参数们):pass
        class 子类(父类):
            def 父类要求实现的方法(self,参数们):
        print('''code''')

 

(二)、多态

  1. 什么是多态:一个类表现出的多种状态,实际上是通过继承来完成的

    • 例子:如果狗类继承动物类,猫类也继承动物类

    • 那么我们就说猫的对象也是动物类型的

    • 狗的对象也是动物类型的

    • 在这个例子里,动物这个类型表现出了猫和狗的形态

  2. 鸭子类型

    在py中,一个类可以是很多类的鸭子类型

    • tuple 元组类

      是可哈希的

      但是又不依靠继承哈希来判定是不是可哈希类型

      元组类是可哈希类型的鸭子类型

       

    • 可迭代的类型

      不是依靠继承迭代来判定是不是迭代类型

      看他长得像(内部实现了__init__

       

    子类继承父类,我们说子类也是父类这个类型的

    ​ 在Python中,一个类是不是属于某一个类型,不仅仅通过继承完成,还可以是不继承,但是如果这个满足了某些类型的特征条件,我们就说它长得像这个类型,那么它就是这个类型的鸭子类型

 

(三)、封装

  1. 封装:就是把属性和方法装起来

    • 广义上的封装:把属性和方法装起来,外面不能直接调用,要通过类的名字来调用

    • 狭义上的封装:把属性和方法藏起来,外面不能调用了,只能在内部偷偷调用

  2. 给一个名字前面加上了双下划綫的时候,这个名字就变成了一个私有的

    所有的私有的内容或者名字都不能在类的外部调用,只能在类的内部使用了

  3. 使用的三种情况

    • 使用的三种情况

      • 不想让你看,也不想让你改


        class User:
           def __init__(self,name,passwd):
               self.usr = name
               self.__pwd = passwd   # 私有的实例变量/私有的对象

        # 没有其他的方法,既不能看,也不能改

         

      • 可以让你看,但不让你改


        class User:
           def __init__(self,name,passwd):
               self.usr = name
               self.__pwd = passwd  # 私有的实例变量/私有的对象属性
           def get_pwd(self):       # 表示的是用户不能改只能看 私有 + 某个get方法实现的
               return self.__pwd
           
        alex = User('alex','sbsbsb')
        print(alex.get__pwd())        # sbsbsb (只能看不能改)

         

      • 可以让你看,也可以让你改(但是要求你按照我的规则改)


        class User:
           def __init__(self,name,passwd):
               self.usr = name
               self.__pwd = passwd  # 私有的实例变量/私有的对象属性
           def get_pwd(self):       # 表示的是用户不能改只能看 私有 + 某个get方法实现的
               return self.__pwd
           def change_pwd(self):    # 表示用户必须调用我们自定义的修改方式来进行变量的修改 私用 + change方法实现
               pass
           

         

    • 封装的语法

      • 私有的静态变量


        # 外部不可以调
        class User:
           __Country = 'China'   # 私有的静态变量
           def func(self):
               print(User.__Country)  # 在类的内部可以调用
        print(User.Country)  # 报错 在类的外部不能调用
        print(User.__Country)# 报错 在类的外部不能调用


        # 内部可以调
        class User:
           __Country = 'China'   # 私有的静态变量
           def func(self):
               print(User.__Country)  # 在类的内部可以调用
        User().func()   # China 说明内部可以调用

         

      • 私有的实例变量


        class User:
           def __init__(self,name,passwd):
               self.usr = name
               self.__pwd = passwd   # 私有的实例变量/私有的对象

         

      • 私有的绑定方法


        import  hashlib
        class User:
           def __init__(self,name,passwd):
               self.usr = name
               self.__pwd = passwd  # 私有的实例变量
           def __get_md5(self):     # 私有的绑定方法
               md5 = hashlib.md5(self.usr.encode('utf-8'))
               md5.update(self.__pwd.encode('utf-8'))
               return md5.hexdigest()
           def getpwd(self):
               return self.__get_md5()
        alex = User('alex','sbsbsb')
        print(alex.getpwd())  
        # d6170374823ac53f99e7647bab677b92

         

      • 私有化目的:所有的私有化都是为了让用户不在外部调用类中的某个名字

      • 私有化作用:如果完成私有化,那么这个类的封装度就更高了,封装度越高各种属性和方法的安全性也越高,但是代码越复杂

      • 私有的特点

        • 能不能类的内部使用? 可以

        • 能不能类的外部使用? 不可以

        • 能不能类的子类中使用? 不可以

      • 类中变量的级别,哪些是python支持的,哪些是python不支持的

        • 共有的

          public 公有的 类内类外都能用,父类子类都能用

          python支持

        • 保护的

          protect 保护的 类内能用,父类子类都能用,类外不能用

          python不支持

        • 私有的

          private 私有的 本类的类内部能用,其他地方都不能用

          python支持

         

         

 

posted on 2019-06-09 15:30  雨后清明  阅读(247)  评论(0编辑  收藏  举报

导航