property伪装属性与面向对象的三大特性之封装、多态和面向对象之反射

派生方法的使用

import datetime
import json

t = {'t1': datetime.datetime.today(),
     't2': datetime.date.today()}
res = json.dumps(t)
print(t)
'''
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type datetime is not JSON serializable
并不是所有数据类型都可以用json序列化的,上述操作就会报错:
    +-------------------+---------------+
    | Python            | JSON          |
    +===================+===============+
    | dict              | object        |
    +-------------------+---------------+
    | list, tuple       | array         |
    +-------------------+---------------+
    | str               | string        |
    +-------------------+---------------+
    | int, float        | number        |
    +-------------------+---------------+
    | True              | true          |
    +-------------------+---------------+
    | False             | false         |
    +-------------------+---------------+
    | None              | null          |
    +-------------------+---------------+
'''
# 1.可以将不符合的数据类型转换为符合的数据类型
t = {'t1': str(datetime.datetime.today()),
     't2': str(datetime.date.today())}
res = json.dumps(t)
print(res)
# {"t1": "2022-07-28 15:30:42.763026", "t2": "2022-07-28"}

# 2.使用派生方法
'''
按住ctrl在鼠标左键点击json可以打开底层
我们可以看到dump底层是函数
def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True,
        allow_nan=True, cls=None, indent=None, separators=None,
        default=None, sort_keys=False, **kw):
其中cls形参如果没有传参就会默认为None
        if cls is None:
            cls = JSONEncoder
        return cls(...)   # JSONEncoder()
cls为None时会给cls绑定一个JSONEncoder,查看JSONEncoder源码
class JSONEncoder(object):
发现序列化报错有default方法触发
def default(self, o):
 raise TypeError(f'Object of type {o.__class__.__name__} '
                        f'is not JSON serializable')
default方法就是报错的结果
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type datetime is not JSON serializable
我们如果想要避免报错 那么肯定需要对default方法做修改(派生)
定义一个JSONEncoder和其内部的default方法,这样在执行时就不会走底层的报错代码,而是走我们的代码
'''
t = {'t1': datetime.datetime.today(),
     't2': datetime.date.today()}


class MYJSONEncoder(json.JSONEncoder):
    def default(self, o):
        'o就是json即将序列化的数据'
        if isinstance(o, datetime.datetime):
            '如果o是datetime.datetime则返回下方'
            return o.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(o, datetime.date):
            '如果o是datetime.date则返回下方'
            return o.strftime('%Y-%m-%d ')
        '如果是可以序列化的类型,那就不做任何处理,让它序列化'
        return super().default(o)


res = json.dumps(t, cls=MYJSONEncoder)
print(res)
# {"t1": "2022-07-28 15:30:03", "t2": "2022-07-28 "}
img

面向对象的三大特性之封装

'''
封装就是将数据或功能隐藏起来
隐藏不是为了让用户无法使用,而是给隐藏的数据开设特定的接口,让用户使用接口才可以使用,我们就可以在接口中添加一些额外操作
在类定义阶段使用双下划线开头的名字,都是隐藏的属性,后续的类和对象都无法直接获取
python不会真正限制任何代码
隐藏的属性实际上也是可以访问,只不过需要做变形处理
__变量名>>>_类名__变量名
但还是不要使用变形的名字取访问,不然就失去了隐藏的意义
'''

class Student(object):
    __school = '霍格沃茨魔法大学'
    '在变量名前面加__就可以将变量名隐藏'
    def __init__(self, name, age):
        self.__name = name
        self.__age = age


res = Student('harry', 20)
print(res.__school)  # 隐藏的变量名无法获取,会直接报错对象没有这个属性
print(res._Student__school)
# 霍格沃茨魔法大学


class Student(object):
    __school = '霍格沃茨魔法大学'
    '在变量名前面加__就可以将变量名隐藏'
    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def check(self):
        '隐藏的变量名只有在类里面才可以获取'
        print(f'''
        学生姓名:{self.__name}
        学生年龄:{self.__age}
        ''')

    def set(self, name, age):
        '隐藏的变量名也只有在类中可以更改'
        if len(name) == 0:
            print('名字不能为空')
            return
        elif not isinstance(age, int):
            print('年龄只能是整数')
            return
        self.__name = name
        self.__age = age


res = Student('harry', 20)
res.check()
# 学生姓名:harry
# 学生年龄:20
res.set('jason', 38)
res.check()
# 学生姓名:jason
# 学生年龄:38
img

property伪装属性

'''
可以理解为将方法伪装成数据
res.name   # 数据只需要点名字
res.func()  # 方法至少还要加括号
伪装之后还可以将res.func()方法伪装成res.func
'''
# 体质指数(BMI)=体重(kg)÷身高^2(m)
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('me', 52, 1.70)
# res = p1.BMI()  # 没伪装的方法使用时要加括号
res = p1.BMI  # 伪装后的方法是数据,数据使用时不能加括号
print(res)


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(f'{value} must be str')
        '若数据类型不是字符串则报错'
        self.__NAME = value
        '若数据类型是字符串则执行'

    @name.deleter
    def name(self):
        raise PermissionError('Can not delete')
    '如果有代码要删除name则报错'


res = Foo('barry')
print(res.name)
# barry
res.name = 233
print(res.name)
# 报错:TypeError: 233 must be str
del res.name
# 报错:PermissionError: Can not delete
img

面向对象三大特性之多态

'''
多态是一种事物的多种形态,但又有相同的功能
猫是喵喵叫,狗是汪汪叫,但都是在叫
'''

class Cat:
    def spark(self):
        print('喵喵喵')


class Dog:
    def spark(self):
        print('汪汪汪')


class Duck:
    def spark(self):
        print('嘎嘎嘎')


res1 = Cat()
res2 = Dog()
res3 = Duck()
print(res1.spark())  # 喵喵喵
print(res2.spark())  # 汪汪汪
print(res3.spark())  # 嘎嘎嘎
'''
像这种有多种形态但相同功能的应该有相同的名字
这样无论是那种动物都可以直接调用
'''


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


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


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


class Duck(Animal):
    def spark(self):
        print('嘎嘎嘎')


res1 = Cat()
res2 = Dog()
res3 = Duck()
res1.spark()  # 喵喵喵
res2.spark()  # 汪汪汪
res3.spark()  # 嘎嘎嘎

'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


ojd = Person()


'''
鸭子类型
只要长得像鸭子,走路像鸭子,说话像鸭子,那就是鸭子
'''

class Teacher:
    def run(self):pass
    def eat(self):pass
class Student:
    def run(self):pass
    def eat(self):pass
'有两个类没有继承任何父类,但他们有共同的功能,那我们在命名时名字就要一样'
    
    
'''
操作系统:一切都是文件
只要能读数据,能写数据,那就是文件
文件都储存在内存或硬盘里
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:一切都是对象
只要有数据,有功能,那就是对象
文件名		文件对象
模块名		模块对象
'''
img

面向对象之反射

'''
反射是通过字符串来操作对象的数据或方法

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

class Student(object):
    school = '霍格沃茨魔法大学'

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


stu = Student()
'''
判断用户提供的名字在不在对象可以使用的范围
stu.school与stu.'school'本质上是不一样的
'''

while True:
    res = input('输入想要查找的名字>>>:').strip()
    print(hasattr(stu, res))  # True
    'hasattr可以判断否有该名字'
    print(getattr(stu, res))  #霍格沃茨魔法大学
    'getattr会获取名字对应的属性'
    if hasattr(stu, res):
        info = getattr(stu, res)
        if callable(res):
            print('拿到的名字是函数:', res())
        else:
            print('拿到的名字是数据', res)
    else:
        print('你要查找的名字,对象没有')


print(stu.__dict__)
# {}
stu.name = 'jason'
stu.age = 18
print(stu.__dict__)
# {'name': 'barry', 'age': 18}
setattr(stu, 'hobby', 'read')
print(stu.__dict__)
# {'name': 'jason', 'age': 18, 'hobby': 'read'}
del stu.name
print(stu.__dict__)
# {'age': 18, 'hobby': 'read'}
delattr(stu, 'age')
print(stu.__dict__)
# {'hobby': 'read'}

实战案例:

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()
posted @ 2022-07-28 20:37  无言以对啊  阅读(33)  评论(0)    收藏  举报