封装,多态,反射

继承下的派生实际应用

  import datetime
  import json

  class MyJsonEncoder(json.JSONEncoder):
      def default(self, o):
          # 形参o就是即将要被序列化的数据对象
          # print('重写了', o)
          '''将o处理成json能够序列化的类型即可'''
          if isinstance(o,datetime.datetime):
              return o.strftime('%Y-%m-%d %X')
          elif isinstance(o, datetime.date):
              return o.strftime('%Y-%m-%d')
          return super().default(o)  # 调用父类的default(让父类的default方法继续执行 防止有其他额外操作)

  d1 = {'t1': datetime.datetime.today(), 't2': datetime.date.today()}
  res = json.dumps(d1, cls=MyJsonEncoder)
  print(res)
  """
  TypeError: Object of type 'datetime' is not JSON serializable
  json不能序列化python所有的数据类型 只能是一些基本数据类型
      json.JSONEncoder

  1.手动将不能序列化的类型先转字符串
      {'t1': str(datetime.datetime.today()), 't2': str(datetime.date.today())}
  2.研究json源码并重写序列化方法
      研究源码发现报错的方法叫default
          raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__)
      我们可以写一个类继承JSONEncoder然后重写default方法
"""

面向对象-------封装

简介

  封装
  1.封装是面向对象编程的一大特点
  2.面向对象编程的第一步 将属性和方法封装到一个抽象的类中
  3.外界使用类创建对象,然后让对象调用方法
  4.对象方法的细节都被封装在类的内部

  说简单的就是隐藏对象的属性和实现细节,仅对外提供公共访问方式。

基础入门 —> setter、getter

  '''
    封装就是指隐藏对象中一些不希望外部所访问到的属性或方法,即为了保证安全

    如何封装,或者说如何隐藏属性或方法?

    在定义类时,将属性或方法名修改为外部并不知道的名称,如下定义类
  '''
  class dog:
      def __init__(self,name,age):
          self.name = name
          self.age = age
      def hello(self):
          print(f'大家好,我是{self.name}')

  #实例化一个对象
  d = dog('哈士奇',3)
  d.hello()

  #直接可以修改类中的属性,不安全
  d.name = '金毛'
  d.hello()

  执行结果
  大家好,我是哈士奇
  大家好,我是金毛

  '''
    那么我们可以这样定义,即内部使用hidden_name来定义属性,外部就不能直接通过name来修改访问类中的属性了
  '''
  class dog:
      def __init__(self,name,age):
          self.new_name = name
          self.new_age = age
      def hello(self):
          print(f'大家好,我是{self.new_name}')

  #实例化一个对象
  d = dog('哈士奇',3)
  d.hello()

  #外部不可以修改类中的属性
  d.name = '金毛'
  d.hello()

  执行结果
  大家好,我是哈士奇
  大家好,我是哈士奇

  '''
    这样外部虽然不能通过name来修改属性值,但是还是可以通过new_name来修改属性值,当然我们是站在上帝的视角来看待这个问题,对于实例化类的对象而言,其并不是完全知道类中的属性与方法

  那么,此时对象如何知道和修改属性的值呢?

  很简单,在类中定义用于修改和获取属性的方法 set_name、get_name

  需要提供一个getter和setter方法使外部可以访问到属性

  getter 获取对象中的指定属性(get_属性名)

  setter 用来设置对象的指定属性(set_属性名)
  '''
  且看如下代码
  class dog:
      def __init__(self,name,age):
          self.new_name = name
          self.new_age = age
      def hello(self):
          print(f'大家好,我是{self.new_name}')

      #用于实例化对象设置修改类属性的方法
      def set_name(self,name):
          self.new_name = name
      #用于实例化对象获取得到类属性的方法
      def get_name(self):
          return self.new_name

  #实例化一个对象
  d = dog('哈士奇',3)
  d.hello()

  #外部使用get_name()方法来修改类中的属性
  d.get_name()
  #外部使用set_name()方法来修改类中的属性
  d.set_name('金毛')
  d.hello()

  '''
    这样做有什么好处呢?

    使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性
  '''
  1.隐藏了属性名,使调用者无法随意的修改对象中的属性
  2.增加了getter和setter方法,很好的控制属性是否是只读的
    如果希望属性是只读的,则可以直接去掉setter方法
    如果希望属性不能被外部访问,则可以直接去掉getter方法
  3.使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
    在方法中判断年龄等属性的数值是否满足正常范围 >0
  4.使用getter方法获取属性,使用setter方法设置属性;可以在读取属性和修改属性的同时做一些其他的
    处理

进阶使用 —> 私有属性

  以上只是根据在面向对象中对封装的简单概述,但是在真实环境中用的不是以上的这些方式,用的是Python
中自动的处理封装的属性,如下所示
  可以为对象的属性使用双下划线开头,__xxx(属性名)
  双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问

  其实隐藏属性只不过是Python自动为属性改了一个名字

  实际上是将名字修改为了,_类名__属性名 比如 __name -> _Person__name,这样还是可以访问的

  防君子不防小人(并不是绝对不能访问,如果一定要访问还是可以通过对象名._类名__属性名来访问)

  class dog:
      def __init__(self,name,age):
          self.__name = name
          self.__age = age
      def hello(self):
          print(f'大家好,我是{self.__name},我{self.__age}岁了')

      #用于实例化对象设置修改类属性的方法
      def set_name(self,name):
          self.__name = name
      #用于实例化对象获取得到类属性的方法
      def get_name(self):
          return self.__name


  #实例化一个对象
  d = dog('哈士奇',3)
  d.hello()
  print(d._dog__name)

  # 其实可以通过如下方式修改私有属性的值,但是不会这么做,因为外部是不可以修改私有属性的值的
  d._dog__name = '金毛'
  d._dog__age = '4'
  d.hello()

  执行结果
  大家好,我是哈士奇,我3岁了
  哈士奇
  大家好,我是金毛,我4岁了

强化加强 —> property

  property装饰器,用来将一个get方法,转换为对象的属性

  添加为property装饰器以后,我们就可以像调用属性一样使用get方法

  使用属性的方式去调用方法,看上去是对属性赋值,但是本质上是调用方法的

  使用property装饰的方法,必须和属性名是一样的

  设置setter前必须设置getter

  class dog:
      def __init__(self,name,age):
          self._name = name
          self._age = age
      def hello(self):
          print(f'大家好,我是{self._name},我{self._age}岁了')

      @property
      def name(self):
          print('get方法执行了~~~')
          return self._name
      # setter方法的装饰器:@属性名.setter
      @name.setter
      def name(self,name):
          print('set方法执行了~~~')
          self._name = name

      @property
      def age(self):
          print('get方法执行了~~~')
          return self._age
      # setter方法的装饰器:@属性名.setter
      @age.setter
      def age(self,age):
          print('set方法执行了~~~')
          self._age = age

  #实例化一个对象
  d = dog('哈士奇',3)
  d.hello()
  print('--'*30)

  # 调用set方法,修改name的值;注意看上去是修改属性名的方式,其实是调用的了方法,注意看输出
  d.name = '德牧'
  d.hello()
  print('--'*30)

  d.age = 4
  d.hello()
  print('--'*30)
  print(d.name)

  # 执行结果
  # 大家好,我是哈士奇,我3岁了
  # ------------------------------------------------------------
  # set方法执行了~~~
  # 大家好,我是德牧,我3岁了
  # ------------------------------------------------------------
  # set方法执行了~~~
  # 大家好,我是德牧,我4岁了
  # ------------------------------------------------------------
  # get方法执行了~~~
  # 德牧

面向对象-------多态

  多态顾名思义多种状态,在python中,不同的对象调用同一个接口,表现出不同的状态,称为多态。

  要实现多态有两个前提:
  1.继承:多态必须发生在父类与子类之间
  2.重写:子类重写父类方法
  
  例:
  class Animal():
      def who(self):
          print("I am an Animal")
  class Duck(Animal):
      def who(self):
          print("I am a duck")

  class Dog(Animal):
      def who(self):
          print("I am a dog")

  class Cat(Animal):
      def who(self):
          print("I am a cat")
  if __name__ == "__main__":
      duck=Duck()
      dog=Dog()
      cat=Cat()
      duck.who()
      dog.who()
      cat.who()
  '''多态的使用增强了程序的灵活性和可扩展性'''

反射

  hasattr():判断对象是否含有字符串对应的数据或者功能
  getattr():根据字符串获取对应的变量名或者函数名
  setattr():根据字符串给对象设置键值对(名称空间中的名字)
  delattr():根据字符串删除对象对应的键值对(名称空间中的名字)
  class Hero(object):
      def __init__(self,name,blood):
          self.name = name
          self.blood = blood
          @property
      def get_name(self):
          return self.name
      @get_name.setter
      def set_name(self,name):
          self.name = name
  hero = Hero('卡莎',1000)


  1.hasattr(object,name)
    判断object中有没有name字符串对应的方法和属性,name可以是函数也可以是参数,还可以是变量,返回结
    果为True、False
    print(hasattr(hero,'name')) # True

  2.getattr(object,name,default=None)
    获取name属性,如果没有会报错,但是添加default属性后,没有name会直接打印default值
    print(getattr(hero,'get_name')) # 卡莎
    print(getattr(hero,'f1')) # <bound method Hero.f1 of <__main__.Hero object at 0x01E6D1C0>>

  3.setattr(object,属性名,属性值)
    返回值是None
    设置属性对象的属性,还可以直接修改当前对象的属性
    print(setattr(hero,'name','女警')) # None
    print(hero.name) # 女警

  4.delattr(object,属性名)
    删除对象属性
    print(hero.__dict__) # {'name': '卡莎', 'blood': 1000}
    delattr(hero,'name')
    print(hero.__dict__) # {'blood': 1000}
posted @ 2022-04-08 22:38  春游去动物园  阅读(43)  评论(0编辑  收藏  举报