小白学python系列————【Day44】面向对象三大特性之封装与多态;面向对象之反射

今日内容概要

  • 派生方法的实战演练(重要)
  • 面向对象三大特性之封装
  • property伪装属性
  • 面向对象三大特性之多态
  • 面向对象之反射(重要)
  • 反射实战(重要)

派生方法的实战演练(重要)

1.前戏

import datetime
import json

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


'''
TypeError: Object of type 'datetime' is not JSON serializable
'''

(1)此处代码会报错,无法正常序列化,原因是json序列化python数据类型是有限制的,并不适用于所有类型。
(2)利用json.JSONEncoder查看json序列化可以适用数据类型
请添加图片描述
(3)上述json存储的为时间类型数据,所以不会被序列化。即将要被序列化的数据,里里外外都必须要保证是上述类型才行。

2.引出
针对上述不能被序列化的数据类型,给出下列解决办法。
(1)解决方式一:
手动将不符合数据类型转成符合要求的。

import datetime
import json
d = {
   't1':str(datetime.datetime.today()),
   't2':str(datetime.date.today())
}
res = json.dumps(d)
print(res)

'''
{"t1": "2022-07-28 15:48:31.269905", "t2": "2022-07-28"}
'''

(2)解决方式二:
利用派生方法。
查看dumps方法后得出:

class JSONEncoder:
   pass
dumps(obj,cls=None):
   if cls == None:
       cls = JSONEncoder
   return cls(...)   # JSONEncoder()

(1)查看JSONEncoder源码发现序列化报错是有default方法触发的

raise TypeError(f'Object of type {o.__class__.__name__} '
                       f'is not JSON serializable')

(2)我们如果想要避免报错 那么肯定需要对default方法做修改(派生)

import datetime
import json
d = {
   't1':datetime.datetime.today(),
   't2':datetime.date.today()
}
class MyJsonEncode(json.JSONEncoder):
   def default(self, o):
       '''o就是json即将要序列化的数据'''
       if isinstance(o, datetime.datetime):
          return o.strftime('%Y-%m-%d %H:%M:%S')
       elif isinstance(o, datetime.date):
           return o.strftime('%Y-%m-%d')
       # 如果是可以序列化的类型 那么不做任何处理 直接让它序列化即可
       return super().default(o)


res = json.dumps(d, cls=MyJsonEncode)
print(res)
json.dumps(d, cls=MyJsonEncode)

面向对象三大特性之封装

1.封装的概念
封装其实就是将数据或者功能隐藏起来;隐藏的目的不是让用户无法使用,而是给这些隐藏的数据开设特定的接口,让用户使用接口才可以去使用,我们在接口中添加一些额外的操作。

2.封装隐藏特性
(1)在类定义阶段使用双下划线开头的名字都是隐藏属性。
后续类和对象都无法直接获取
(2)在python中不会真正的限制任何代码。
隐藏的属性如果真的需要访问,也可以经过变形处理。不过这样就失去了隐藏的意义!!!

3.封装实例——代码展示

class Student(object):
   __school = '清华大学'
   def __init__(self, name, age):
       self.__name = name
       self.__age = age
   # 专门开设一个访问学生数据的通道(接口)
   def check_info(self):
       print("""
       学生姓名:%s
       学生年龄:%s
       """ % (self.__name, self.__age))
   # 专门开设一个修改学生数据的通道(接口)
   def set_info(self,name,age):
       if len(name) == 0:
           print('用户名不能为空')
           return
       if not isinstance(age,int):
           print('年龄必须是数字')
           return
       self.__name = name
       self.__age = age

stu1 = Student('jason', 18)
print(stu1._Student__name)
''' jason '''
# 修改名字方法:
stu1.set_info('kevin',20)
print(stu1._Student__name)
''' kevin'''

4.代码封装之君子协定
我们在编写python很多时候都是大家墨守成规的东西,不需要真正的限制(君子协定):

class A:
	_school = '清华大学'
	def  _choice_course(self):
	pass

python伪装属性

1.伪装的含义
可以简单地理解为将方法伪装成数据。
数据只需要点名字:obj.name
方法至少还要加括号:obj.func()
伪装之后可以将func方法伪装成数据:obj.func

2. @property主要功能作用
具体案例代码展示:
(1)# 没有加装饰器@property之前————p1.BMI() #加括号调用

class Person:
   def __init__(self, name, weight, height):
       self.name = name
       self.weight = weight
       self.height = height

   def BMI(self):
       return self.weight / (self.height ** 2)

# 没有加装饰器@property之前
p1 = Person('jason',57,1.73)
res = p1.BMI()   # 加括号调用
print(res)

''' 19.045073340238563'''
"""BMI虽然需要计算获得 但是更像是人的数据"""

(2)# 添加装饰器@property之后————p1.BMI #直接调用

class Person:
   def __init__(self, name, weight, height):
       self.name = name
       self.weight = weight
       self.height = height

   @property
   def BMI(self):
       return self.weight / (self.height ** 2)
# 添加装饰器@property之后
p1 = Person('jason', 78, 1.83)
print(p1.BMI)      # 23.291229956104985
print(p1.name)     # jason     

3.知识点的加深之伪装的彻底(了解)
需求:增加了用户修改和删除装饰器

class Foo:
   def __init__(self, val):
       self.__NAME = val  # 将属性隐藏起来

   @property
   def name(self):
       return self.__NAME

   @name.setter
   def name(self, value):
       if not isinstance(value, str):  # 在设定值之前进行类型检查
           raise TypeError('%s must be str' % value)
       self.__NAME = value  # 通过类型检查后,将值value存放到真实的位置self.__NAME

   @name.deleter
   def name(self):
       raise PermissionError('Can not delete')


obj = Foo('jason')
print(obj.name)    # jason
obj.name = 'kevin'
print(obj.name)    # kevin

del obj.name    
# PermissionError: Can not delete

面向对象三大特性之多态

1.多态的字面含义
多态:一种事物的多种形态
水:液态 气态 固态
动物:人 狗 猫 猪

2.具体实例代码展示
(1)案例一:
前戏之动物叫声单独方法调用:

class Animal(object):
   def spark(self):
       pass

class Cat(Animal):
   def miao(self):
       print('喵喵喵')
class Dog(Animal):
   def wang(self):
       print('汪汪汪')

#
c1 = Cat()
d1 = Dog()
c1.miao()     # 喵喵喵
d1.wang()     # 汪汪汪
"""
一种事物有多种形态,但是相同的功能应该有相同的名字;
这样的话以后我们无论拿到具体的动物,都不需要知道到底是哪种动物,>直接调用相同的功能即可。
"""

引出之不同动物叫声用一个方法(spark)调用:

"""
eg:
无论是鸡鸭狗猪,只要叫就调用固定的属于叫的功能!!!
"""
class Animal(object):
   def spark(self):
       pass

class Cat(Animal):
   def spark(self):
       print('喵喵喵')
class Dog(Animal):
   def spark(self):
       print('汪汪汪')

c1.spark()    # 喵喵喵
d1.spark()    # 汪汪汪

(2)案例二:
不论是元组,列表还是字典,统计长度都是len方法调用。

l1 = [11, 22, 33, 44]
d1 = {'name': 'jason', 'pwd': 123, 'hobby': 'raed'}
t1 = (11, 22, 33, 44)
print(len(l1))
print(len(d1))
print(len(t1))

3.多态性的概念
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
   def run(self):
       pass
obj = Person()

4.鸭子类型
大白话解释:只要你长得像鸭子,走路像鸭子,说话像鸭子,那么你就是鸭子。

class Teacher:
   def run(self):pass
   def eat(self):pass
class Student:
   def run(self):pass
   def eat(self):pass

5.多态不同系统中解释(鸭子类型推导)
(1)linux系统:一切皆文件
只要你能读数据 能写数据 那么你就是文件
内存
硬盘

class Txt:  # Txt类有两个与文件类型同名的方法,即read和write
   def read(self):
       pass

   def write(self):
       pass

class Disk:  # Disk类也有两个与文件类型同名的方法:read和write
   def read(self):
       pass

   def write(self):
       pass

class Memory:  # Memory类也有两个与文件类型同名的方法:read和write
   def read(self):
       pass

   def write(self):
       pass

(2)python:一切皆对象
只要你有数据 有功能 那么你就是对象
文件名 文件对象
模块名 模块对象

面向对象之反射

1.反射的含义
通过字符串来操作对象的数据或方法

2.反射四个方法:
hasattr():判断对象是否含有某个字符串对应的属性
getattr():获取对象字符串对应的属性
setattr():根据字符串给对象设置属性
delattr():根据字符串给对象删除属性

3. 反射实例代码展示

class Student:
   school = '清华大学'

   def choice_course(self):
       print('选课')


stu = Student()
# 需求:判断用户提供的名字在不在对象可以使用的范围内
# 方式1:利用异常处理(过于繁琐)
# try:
#     if stu.school:
#         print(f"True{stu.school}")
# except Exception:
#     print("没有属性")
"""
变量名school 与字符串school 区别大不大
   stu.school
   stu.'school'
两者虽然只差了引号 但是本质是完全不一样的
"""
# 方式2:获取用户输入的名字 然后判断该名字对象有没有
# while True:
#     target_name = input('请输入您想要核查的名字>>>:').strip()
#     '''上面的异常更加不好实现 需要用反射'''
#     # print(hasattr(stu, target_name))
#     # print(getattr(stu, target_name))
#     if hasattr(stu, target_name):
#         # print(getattr(stu, target_name))
#         res = getattr(stu, target_name)
#         if callable(res):
#             print('拿到的名字是一个函数', res())
#         else:
#             print('拿到的名字是一个数据', res)
#     else:
#         print('不好意思 您想要查找的名字 对象没有')
print(stu.__dict__)
stu.name = 'jason'
stu.age = 18
print(stu.__dict__)
setattr(stu, 'gender', 'male')
setattr(stu, 'hobby', 'read')
print(stu.__dict__)
del stu.name
print(stu.__dict__)
delattr(stu, 'age')
print(stu.__dict__)

4.反射用法
需求里面只要看到…字符串或者…对象那么肯定需要使用反射!!!

5.反射实战案例


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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值