编程语言
首页 > 编程语言> > 面向对象编程——派生实际应用、封装、多态、反射

面向对象编程——派生实际应用、封装、多态、反射

作者:互联网

派生实际应用

import datetime
import json
# 有一个字典d1 现在要将他序列化成json格式的字符串
d1 = {'t1': datetime.datetime.today(), 't2': datetime.date.today()}
res = json.dumps(d1)
# 发现报错
print(res)  # TypeError: Object of type datetime is not JSON serializable
# 原因:datatime类型不能被序列化

解决方法一:将datatime类型转化为str类型

# 通过str()转为str类型
import datetime
import json
d1 = {'t1': str(datetime.datetime.today()), 't2': str(datetime.date.today())}
res = json.dumps(d1)
print(res)  # {"t1": "2022-04-08 20:39:18.242301", "t2": "2022-04-08"}

解决方法二:研究json源码并重写序列化方法

'''
    研究源码发现报错的方法叫default
        raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__)
    我们可以写一个类继承JSONEncoder然后重写default方法
'''
import datetime
import json

class MyJsonEncoder(json.JSONEncoder):
    def default(self, o):
        # o为想要被序列的的数据对象
        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(MyJsonEncoder, self).default(o)

d1 = {'t1': datetime.datetime.today(), 't2': datetime.date.today()}
res = json.dumps(d1, cls=MyJsonEncoder)
print(res)

面向对象三大特征之封装

封装的含义
     将类中的某些名字'隐藏'起来 不让外界调用
        隐藏的目的是为了提供专门的通道去访问 在通道里可以添加额外的功能
class Student(object):
    school = 'xx幼儿园'
    __label = '有钱人家的孩子'
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def choose_class(self):
        print(f'{self.name} 正在选班')

stu1 = Student('小葵', 4)
print(stu1.name)    # 小葵
print(stu1.age)     # 4
print(stu1.school)  # xx幼儿园

print(stu1.__label)  # AttributeError: 'Student' object has no attribute '__label'  
# 这里我们调用__label的时候 报错说不含__label属性 可我们类内明明有'''__label = '有钱人家的孩子''''
# 这时我们查看类的名称空间
print(Student.__dict__)
# {'__module__': '__main__', 'school': 'xx幼儿园', '_Student__label': '有钱人家的孩子', '__init__': <function Student.__init__ at 0x000001D379AD63A0>, 'choose_class': <function Student.choose_class at 0x000001D379AD68B0>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
# 发现内含  '_Student__label': '有钱人家的孩子'   可发现__label 名字变为了 _Student__label
# 尝试使用
print(stu1._Student__label)     # 有钱人家的孩子
print(Student._Student__label)  # 有钱人家的孩子
如何封装名字?
    在变量名的前面加上两个下划线__

封装的功能只在类定义阶段才能生效!!!
    在类中封装其实也不是绝对的 仅仅是做了语法上的变形而已
        __变量名       >>>     _类名__变量名
我们虽然指定了封装的内部变形语法 但是也不能直接去访问
    看到了就表示这个属性需要通过特定的通道(接口)去访问
class Employee(object):
    __label = '员工'
    def __init__(self, name, base_pay, merit_pay):
        self.__name = name
        self.__base_pay = base_pay
        self.__merit_pay = merit_pay
    # 开设一个访问员工数据的通道(接口)
    def view(self):
        print(f'''
            员工: {self.__name}
            底薪: {self.__base_pay}
            绩效: {self.__merit_pay}
        ''')
    # 开设一个修改员工数据的推导(接口)
    def change_info(self, name, base_pay, merit_pay):
        if len(name) == 0:
            print('用户名不能为空')
            return
        if base_pay <=9000:
            print('底薪必须大于9000 不要那么小气')
            return
        if not isinstance(merit_pay, int):
            print('绩效必须是整数')
            return

        self.__name = name
        self.__base_pay = base_pay
        self.__merit_pay = merit_pay

obj1 = Employee('cc', 10000, 6000)
obj1.view()
obj1.change_info('', 10000, 2000)    # 用户名不能为空
obj1.change_info('cc', 1000, 2000)   # 底薪必须大于9000 不要那么小气
obj1.change_info('cc', 10000, 'a')   # 绩效必须是整数
obj1.change_info('aa', 20000, 2600)
obj1.view()
将数据隐藏起来就限制了类外部对数据的直接操作
  此时,类内一个提供相应的接口来允许类外部对数据的间接操作
    我们还可以给接口附加一些额外的逻辑来使对时局的操作进行严格控制

property

    property能将方法伪装成数据
# 有时候我们的数据是需要通过计算所得
# 可这些数据本应该是数据而不是一个计算功能
class Employee(object):
    def __init__(self, name, base_pay, merit_pay, overtime_pay):
        self.__name = name
        self.__base_pay = base_pay
        self.__merit_pay = merit_pay
        self.__overtime_pay = overtime_pay

    def salary(self):
        print(f'总薪资为: {self.__base_pay + self.__merit_pay + self.__overtime_pay}')

e1 = Employee('cc', 20000, 6000, 4000)
e1.salary()     # 总薪资为: 30000
'''这里的salary拥有计算并打印总薪资的功能 而工资本应该看作是一个数据返回而不是一个计算的功能'''
class Employee(object):
    def __init__(self, name, base_pay, merit_pay, overtime_pay):
        self.__name = name
        self.__base_pay = base_pay
        self.__merit_pay = merit_pay
        self.__overtime_pay = overtime_pay
    @property
    def salary(self):
        return self.__base_pay + self.__merit_pay + self.__overtime_pay
e1 = Employee('cc', 20000, 6000, 4000)
print(e1.salary)    # 30000

面向对象三大特性之多态

    多态就是一种事物多种形态的意思
# 多态性
	class Animal(object):
    def speak(self):
        pass

  class Cat(Animal):
      def speak(self):
          print('喵喵喵')

  class Dog(Animal):
      def speak(self):
          print('汪汪汪')

  class Pig(Animal):
      def speak(self):
          print('哼哼哼')

c1 = Cat()
d1 = Dog()
p1 = Pig()
c1.speak()
d1.speak()
p1.speak()
'''
  简单理解就是 : 猫、狗、猪都属动物 那么他们都有叫的功能 只是叫法不同 
      那么我们可以定义一个相同的方法用于叫的时候调用
            其他动物叫也可以调用同一方法

多态性的好处在于增强了程序的灵活性和可扩展性,
  比如通过继承Animal类创建了一个新的类,
    实例化得到的对象obj,可以使用相同的方式使用obj.speak()
'''
# python里提供了强制性的此时来实现多态性(不推荐使用)
import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
    def talk(self): # 抽象方法中无需实现具体的功能
        pass
class Person(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
    def talk(self):
        pass
p1=Person() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化
"""
由多态性衍生出一个鸭子类型理论
    只要你看着像鸭子 走路像鸭子 说话像鸭子 那么你就是鸭子!!!
"""

# 鸭子类型的实战案例
"""
在linux系统中有一句话>>>:一切皆文件!!!
    内存可以存取数据
    硬盘可以存取数据
    ...
    那么多有人都是文件
"""
class Memory(object):
    def read(self):
        pass
    def write(self):
        pass
class Disk(object):
    def read(self):
        pass
    def write(self):
        pass
# 得到内存或者硬盘对象之后 只要想读取数据就调用read 想写入数据就调用write 不需要考虑具体的对象是谁

面向对象之反射

# 什么是反射
      指程序可以访问、检测和修改本身状态或者行为的一种能力
      其实就是通过字符串来操作对象的数据和功能
    
# 反射需要掌握的四个方法
  hasattr():判断对象是否含有字符串对应的数据或者功能
  getattr():根据字符串获取对应的变量名或者函数名
  setattr():根据字符串给对象设置键值对(名称空间中的名字)
  delattr():根据字符串删除对象对应的键值对(名称空间中的名字)
class Student(object):
    school = 'xx幼儿园'
    def get(self):
        pass

print(Student.__dict__)

print(hasattr(Student, 'school'))   # True
print(hasattr(Student, 'get'))      # True
print(hasattr(Student, 'name'))     # False

print(getattr(Student, 'school'))   # xx幼儿园
print(getattr(Student, 'get'))      # <function Student.get at 0x000001A532FD63A0>
print(getattr(Student, 'csb'))      # AttributeError: type object 'Student' has no attribute 'csb'

setattr(Student, '学费', 20000)
print(Student.__dict__)

delattr(Student, 'school')
print(Student.__dict__)
# 编写一个小程序 判断Student名称空间中是否含有用户指定的名字 如果有则取出展示
class Student(object):
    school = 'xx幼儿园'
    def get(self):
        pass

guess_name = input('请输入查找的名字>>>:').strip()
if hasattr(Student, guess_name):
    target = getattr(Student, guess_name)
    if callable(target):
        print(f'类中有一个功能名字{guess_name}')
    else:
        print(f'类中有一个数据名字{guess_name}')
else:
    print('不存在该名字')
以后只要在业务中看到关键字 
        对象 和 字符串(用户输入、自定义、指定) 那么肯定用反射
# 实例
# 利用反射获取配置文件中的配置信息
"""一切皆对象 文件也是对象"""
import settings
dir(settings)  # 获取对象中所有可以使用的名字
getattr(settings, 'NAME')

class FtpServer:
     def serve_forever(self):
         while True:
             inp=input('input your cmd>>: ').strip()
             cmd,file=inp.split()
             if hasattr(self,cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
                 func=getattr(self,cmd) # 根据字符串cmd,获取对象self对应的方法属性
                 func(file)
     def get(self,file):
         print('Downloading %s...' %file)
     def put(self,file):
         print('Uploading %s...' %file)

obj = FtpServer()
obj.serve_forever()

标签:__,.__,封装,Student,pay,self,多态,面向对象编程,print
来源: https://www.cnblogs.com/run-fast-hit-fast-win-fast/p/16120264.html