面向对象之封装,多态与反射

派生实战

1.案例
import datetime
import json
# dic = {
#     'a1' : datetime.datetime.today(),
#     'a2' : datetime.date.today()
# }
# res = json.dumps(dic)
# print(res)
# 报错类型
"""
Traceback (most recent call last):
  File "D:/pythonProject/day33/01派生的实战.py", line 7, in <module>
    res = json.dump(dic)
TypeError: dump() missing 1 required positional argument: 'fp'

"""
# 可序列化对象
"""
  +-------------------+---------------+
    | Python            | JSON          |
    +===================+===============+
    | dict              | object        |
    +-------------------+---------------+
    | list, tuple       | array         |
    +-------------------+---------------+
    | str               | string        |
    +-------------------+---------------+
    | int, float        | number        |
    +-------------------+---------------+
    | True              | true          |
    +-------------------+---------------+
    | False             | false         |
    +-------------------+---------------+
    | None              | null          |
    +-------------------+---------------+
"""
# 在python中的json模块并不能将所有格式的东西全部都进行序列化,那么我们想要将上方调用现在时间的功能进行序列化就需要改变进行存入
2.方法一:将上方功能转成字符串格式进行存储就可以实现
dic = {
    'a1' : str(datetime.datetime.today()),
    'a2' : str(datetime.date.today())
}
# res = json.dumps(dic)
# print(res)  # {"a1": "2022-07-28 14:57:37.818571", "a2": "2022-07-28"}
# 这样就可以将对象进行序列化,并读出
3.方法二:使用派生类进行修改
# 根据报错查看那里出了问题于是点dumps进入查看源代码,发现哪里可以修改,饭后发现下方有一个如果cls是空的话那么就可以修改,JSONEncoder发现这个然后我们去查看内部关于他的源代码然后发现可以找到他可以接受什么类型的元素,然后我们在下面发现了自动捕获报错那么他就是我们可以突破的点,然后我们回到我们的代码中进行修改使用派生的方式自己内部有差不多的代码那么就可以截取, 修改, 返回让他可以接受我们的代码并进行序列化然后我们反击派生即可修改
class MyJsonEncoder(json.JSONEncoder):  #调用父类对象
    def default(self, o):  # 找到可以修改项
        if isinstance(o, datetime.datetime):  # 如果对象是o的话怎么怎么样
            return o.strftime('%Y-%m-%d %H:%M:%S %W')  # 返回现在的时间但是是需要格式化输出
        elif isinstance(o, datetime.date):  # 对象o
            return o.strftime('%Y-%m-%d')  # 输出
        return super().default(o)  # 返回派生

res = json.dumps(dic, cls=MyJsonEncoder)  # 序列化对象并赋值
print(res)  # {"a1": "2022-07-28 15:15:41.348170", "a2": "2022-07-28"}
json.dumps(dic, cls=MyJsonEncoder)  # 序列化

"""
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):
        # cached encoder
        if (not skipkeys and ensure_ascii and
                check_circular and allow_nan and
                cls is None and indent is None and separators is None and
                default is None and not sort_keys and not kw):
            iterable = _default_encoder.iterencode(obj)
        else:
            if cls is None:
                cls = JSONEncoder
            iterable = 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).iterencode(obj)
        # could accelerate with writelines in some versions of Python, at
        # a debuggability cost
        for chunk in iterable:
            fp.write(chunk)
        
        
        
            def default(self, o):
           Implement this method in a subclass such that it returns
        a serializable object for ``o``, or calls the base implementation
        (to raise a ``TypeError``).

        For example, to support arbitrary iterators, you could
        implement default like this::

            def default(self, o):
                try:
                    iterable = iter(o)
                except TypeError:
                    pass
                else:
                    return list(iterable)
                # Let the base class default method raise the TypeError
                return JSONEncoder.default(self, o)

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

image

面向对象三大特性之封装

1.封装其实就是将数据或者功能给她隐藏起来用户虽然也可以使用但是必须需要一些额外的操作打开对应的接口才可以去使用,而我们则需要在接口中添加操作去将他隐藏起来。
2.在类的定义中使用双下滑线开头的都是拥有隐藏属性的,后续的类或者对象都无法直接去获取和调用它
3.在我们的python中不会真正限制任何的代码,非常的贴心,所以我们要访问隐藏对象的时候也不是不能访问只是需要进行特殊处理一下
	需要隐藏的数据:__变量名  >>>  调用数据: _类名__变量名 
4.案例
class Student(object):
    __school = '中国传媒大学'
    ## 公共功能
    def __init__(self, name, age, hobby):
        self.__name = name
        self.__age = age
        self.__hobby = hobby
    # 接受学生信息
    def check_info(self):
        print("""
        学生姓名:%s
        学生年龄:%s
        学生爱好:%s
        """ %(self.__name, self.__age, self.__hobby))
    # 学生修改信息
    def set_info(self, name , age, hobby):
        if len(name) == 0:
            print('用户名不能为空')
            return
        if not isinstance(age, int):
            print('年龄必须是数字')
            return
        if len(hobby) == 0:
            print('爱好不能为空')
            return
        self.__name = name
        self.__age = age
        self.__hobby = hobby
stu = Student('joseph', 21, 'read')
print(stu)
print(stu.check_info)
print(stu.set_info('小娴是你嘛', 21, '今天刚好一年难受'))

image

propert伪装

1.可以理解为将方法伪装成数据
	obj.name  # 我们在调用数据的时候是需要使用句点符点的方式就可以调用
   obj.func()  # 而我们在调用方法的时候就需要给她加一个括号来调用,但是现在当我们有了伪装的时候只需要像调用数据一样只点一下就可以直接调用,给出一种错觉,这就是伪装
	obj.func  # 伪装后真假难辨,obj.func,雄兔脚朴素,雌兔眼迷离,双兔傍低走,安能辨我是雄雌。
    
身体健康指标:
	体质指数(BMI) = 体重(kg) ➗ 身高 **2(m)
但是我感觉我好像不太健康的样子,太轻了
2.
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('joseph', 60, 178)
res = p1.BMI
print(res)
print(p1.BMI)
print(p1.name)
print(p1.weight)
print(p1.height)
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')  # 如果要删除name则报错

obj = Foo('joseph')
print(obj.name)  # joseph
obj.name = 999  # 返回第一个报错也就是必须为字符串我们给她修改成整型
print(obj.name)  # 打印  报错必须为字符串
del obj.name # 如果要删除的话直接捕获让他报错删除不了

image

面向对象三大特性之多态

1.什么是多态
	多态其实就是一种事物的多种形态
    就像水就有三种形态:固态(冰), 气态(水蒸气), 液态(肥宅快乐水)
    动物也分为门,纲, 目, 科, 属, 种等分类
2.案例
# 警察抓罪犯,警察和两条警犬打击犯罪警犬分两种一种追击敌人另一种搜寻毒品,携带不同的警犬执行不同的工作
class Dog(object):  # 定义父类
    def work(self):  # 提供解决方法
        pass
# 定义子类,子类重写父类代码,并定义派生对象
class ArmyDog(Dog):  # 继承上方父类
    def work(self):  # 重写同名的代码
        print('追击罪犯...')
class DrugDog(Dog):
    def work(self):
        print('追击毒品...')
# 定义人类
class Person(object):
    def work_with_Dog(self, dog):  # 传入不同的类型,生成不通过的类型
        dog.work()  # 调用
ad = ArmyDog()  # 调用不同对象
dd = DrugDog()

police = Person()
police.work_with_Dog(ad)  # 追击罪犯...
police.work_with_Dog(dd)  # 追击毒品...
3.多态的本质
	多态其实就是很多类共同调用同一个类,但是这些近似的功能都有共同的名字,我们无论拿到什么东西只要是可以套用的就可以直接去调用
4.抽象类
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.鸭子类型
鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定
“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”
我们并不关心对象是什么类型,到底是不是鸭子,只关心行为
鸭子类型在动态语言中经常使用,非常灵活,使得python不想java那样专门去弄一大堆的设计模式

image

文件属性

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:一切皆对象
        只要你有数据 有功能 那么你就是对象
            文件名         文件对象
            模块名         模块对象       

image

反射方法

1.反射是什么
	反射其实就是一种使用字符串来操作对象数据和方法的的方式
2.反射的使用方法
	hasattr():判断对象是否含有某个字符串对应的属性
   getattr():获取对象字符串对应的属性
	setattr():根据字符串给对象设置属性
   delattr():根据字符串给对象删除属性
3.getattr(核心)

判断类、对象或者模块中是否有相应的属性或方法。用法:getattr(obj,str,default=None) 判断obj中是否有str属性,有就返回,没有时有传入第三参数就返回第三参数,没有就报错。
class A:
    name = 'joseph'
    age = 21
print(getattr(A, 'name'))  # joseph
print(getattr(A, 'language', 'notfound'))  # notfound
4.setattr()

设置属性。第三参数为新的属性值
class A:
    name = 'joseph'
    age = 21
setattr(A, 'name', 'Alice')
setattr(A,'language', 'Chinese')
print(getattr(A, 'name'))  # Alice
print(getattr(A, 'language', 'notfound'))  # Chinese

5.hasattr
判断时候有某个属性,有就返回True,没有就返回False
class A:
    name = 'joseph'
    age = 21
if hasattr(A, 'name'):
    print(getattr(A, 'name'))  # joseph
6.delattr
删除某个属性
class A:
    name = 'joseph'
    age = 21

delattr(A, 'name')
print(hasattr(A, 'name'))  # False
7.反射本文件
sys.modulses[__ name__]就是本文件对象。
import sys
class A:
    def __repr__(self):
        return '666'
if hasattr(sys.modules[__name__], 'A'):
    print(getattr(sys.modules[__name__], 'A'))  # <class '__main__.A'>  # # 666

image

反射的实际应用

1.反射的用途
	1、反射类中的变量 : 静态属性,类方法,静态方法
	2、反射对象中的变量、对象属性、普通方法
	3、 反射模块中的变量
	4、反射本文件中的变量
2.应用
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()

image

posted @ 2022-07-28 20:18  Joseph-bright  阅读(120)  评论(0编辑  收藏  举报