派生、封装、property伪装属性、多态、反射
派生方法的实战演练
序列化字典
import json
import datetime
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序列化的有:字典、列表、元组、字符串、整型、浮点型、布尔型(true、false、none)
ps:即将要被序列化的数据,里里外外都必须是上述类型才可以
将上述字典在不报错的情况下序列化
方式一
手动将不符合数据类型要求的数据转成符合要求的
d = {'t1':str(datetime.datetime.today()),
't2':str(datetime.date.today())
}
res = json.dumps(d)
print(res ) # {"t1": "2022-07-28 14:50:01.971910", "t2": "2022-07-28"}
方式二
用派生的方式
1.查看dumps源码
def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False, **kw)
2.找到 cls = None,默认形参
if cls is None:
cls = JSONEncoder
return cls(
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators, default=default, sort_keys=sort_keys,
**kw).encode(obj)
>>>默认是调用JSONEncoder类,并且产生obj对象调用JSONEncoder类里面的encode()方法
3.在JSONEncoder类中,有default方法,发现与我们之前报错信息一致,说明报错是default方法触发的
def default(self, o):
raise TypeError("Object of type '%s' is not JSON serializable" %
o.__class__.__name__)
4. 我们如果想要避免报错,那么需要对default方法做出修改
d = {'t1':datetime.datetime.today(),
't2':(datetime.date.today())
}
class My_JSONEncoder(json.JSONEncoder):
def default(self, 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) # 如果是可序列化的数据类型,不做任何操作
res = json.dumps(d,cls = My_JSONEncoder) # 使用自己定义的类
print(res) # {"t1": "2022-07-28 15:10:14", "t2": "2022-07-28"}
面向对象三大特性之封装
封装的简介
1.封装其实就是将数据或者功能隐藏起来
2.隐藏得到目的:不是让用户无法使用,而是给这些隐藏的数据开设特定的接口,让用户使用接口才可以去使用,我们只需在接口中添加一些额外的操作
3.在类定义阶段使用双下划线开头的名字,都是隐藏的属性后续类和对象无法直接获取
eg:
class Student:
school = '清华'
__class = '信管'
def course(self):
print("选课")
def __info(self):
print("信息")
stu1 = Student()
print(stu1.school) # 清华
print(stu1.__class) # 'Student' object has no attribute '__class'
print(stu1.course()) # 选课
print(stu1.__info) # 'Student' object has no attribute '__info'
4.在python中不会真正的限制任何代码,隐藏的属性如果真的需要访问,只需要:_类名__变量名
eg:
stu1 = Student()
print(Student.__dict__) # {'__module__': '__main__', 'school': '清华', '_Student__class': '信管', 'course': ..., '_Student__info':...}
print(Student._Student__class) # 信管
print(Student._Student__info(stu1)) # 信息
ps:既然隐藏了,就不应该使用变形之后的名字去访问,否则就失去的隐藏的意义
实例
提供接口查看,修改数据
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('nana',18)
# print(stu1.__name) # 'Student' object has no attribute '__name'
print(stu1.check_info()) # 学生姓名:nana 学生年龄:18
print(stu1.set_info("xiao",20))
print(stu1.check_info()) # 学生姓名:xiao 学生姓名:xiao
property伪装数据
将方法伪装成数据,之前获取数据:“obj.变量名”的方式,获取方法"obj.方法名()"。将方法伪装成数据之后只需要“obj.方法名”
实例1 --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)
p1 = Person('nana',44,1.57)
print(p1.bmi()) # 17.85062274331616
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)
p1 = Person('nana',44,1.57)
print(p1.bmi()) # 报错 因为已经伪装成数据了 'float' object is not callable
print(p1.bmi) # 17.85062274331616
实例2
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')
fool = Foo('nana')
print(fool.name) # nana
# fool.name = 1
# print(fool.name) # TypeError: 1 must be str
fool.name = 'xiao'
print(fool.name) # xiao
del fool.name # PermissionError: Can not delete
面向对象三大特性之多态
多态指一种事物的多种状态;在编写代码时,不同的类可能有很多相同的功能,相同的功能也应该有相同的名字,我们可以通过在父类引入抽象类的概念来硬性限制子类必须要有某些方法名
实例1
class Animal(object):
def spark(self):
pass
class Cat(Animal):
def spark(self):
print('喵喵喵')
class Dog(Animal):
def spark(self):
print('汪汪汪')
class Pig(Animal):
def spark(self):
print('哼哼哼')
c1 = Cat()
d1 = Dog()
p1 = Pig()
c1.spark() # 喵喵喵
d1.spark() # 汪汪汪
p1.spark() # 哼哼哼
实例2
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()
鸭子类型
制造出外观和行为相同的对象,同样可以实现不考虑对象类型而使用对象,称为鸭子类型。也就是:只要长得像鸭子,走路像鸭子,说话想鸭子,那么就是鸭子
>>>老师和学生,都可以跑(方法),都需要(吃饭),此时就可以用相同的方法名
class Teacher:
def run(self):pass
def eat(self):pass
class Student:
def run(self):pass
def eat(self):pass
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
python:一切皆对象
只要有数据、功能,那么就是对象。(文件名>>>文件对象 模块名>>>模块对象)
面向对象之反射
反射就是通过字符串来操作对象的数据或方法
反射的主要方法
hasattr():判断对象是否含有某个字符串对应的属性
getattr():获取对象字符串对应得属性
setattr():根据字符串给对象设置属性
delattr():根据字符串给对象删除属性
实例1
需求:判断用户提供的名字在不在对象可以使用的范围内
class Student:
school = '清华大学'
def choice_course(self):
print('选课')
stu = Student()
方式1:利用异常处理
class Student:
school = '清华大学'
def choice_course(self):
print('选课')
stu = Student()
try:
if stu.school:
print(f"True{stu.school}")
except Exception:
print("没有属性")
方式2:获取用户输入的名字,然后判断该名字对象有没有
class Student:
school = '清华大学'
def choice_course(self):
print('选课')
stu = Student()
while True:
cmd_name = input("请输入你想要查询的名字>>>:").strip()
if hasattr(stu,cmd_name):
res = getattr(stu,cmd_name)
# print(res)
if callable(res):
print("这个是一个函数哦")
else:
print("这个是个数据哦")
else:
print("没有你想要查找的名字")
setattr()、delattr()用法
class Student:
school = '清华大学'
def choice_course(self):
print('选课')
stu = Student()
stu.name = 'nana'
print(stu.name) # nana
stu.hobby = 'read'
setattr(stu,'name','xiao')
print(stu.name) # xiao
print(stu.__dict__) # {'name': 'xiao', 'hobby': 'read'}
delattr(stu,'name')
print(stu.__dict__) # {'hobby': 'read'}
实例3
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()
浙公网安备 33010602011771号