ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

派生,三大特性之封装、多态,property伪装、反射

2022-07-30 14:05:28  阅读:234  来源: 互联网

标签:__ name self 多态 三大 print property class def


目录

一、派生实战演练

1.代码发生错误

import datetime
import json

d = {
    't1': datetime.datetime.today(),
    't2': datetime.datetime.today()
}
res = json.dumps(d)
print(res)
'''
上述代码会报错:
    raise TypeError(f'Object of type {o.__class__.__name__} '
    TypeError: Object of type datetime is not JSON serializable
json序列化python数据型是有限制的,不是所有的类型都可以
要被序列化的数据,里里外外都必须是下面类型才可以(利用查看源码的方式查看JSONEncoder)
json.JSONEncoder
    +-------------------+---------------+
    | Python            | JSON          |
    +===================+===============+
    | dict              | object        |
    +-------------------+---------------+
    | list, tuple       | array         |
    +-------------------+---------------+
    | str               | string        |
    +-------------------+---------------+
    | int, float        | number        |
    +-------------------+---------------+
    | True              | true          |
    +-------------------+---------------+
    | False             | false         |
    +-------------------+---------------+
    | None              | null          |
    +-------------------+---------------+

'''

2.解决方法

解决方式一:手动将不符合数据类型要求的数据转换成符合要求的
import datetime
import json

d = {
    't1': str(datetime.datetime.today()),
    't2': str(datetime.datetime.today())
}
res = json.dumps(d)
print(res)
解决方式二:利用派生方法
什么是派生:子类继承父类并且在子类中写了一个与父类相同的方法,重写了,然后利用super方法去调用父类的方法,在调用的过程中可以再添加一些功能
d = {
    't1': datetime.datetime.today(),
    't2': datetime.datetime.today()
}
res = json.dumps(d)
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):
        ...
'''
obj用来接收传过来的数据
1.cls=None是一个默认值形参
2.class JSONEncoder:  # JSONEncoder是一个类
    	pass
	dumps(obj,cls=None):
        if cls == None:
            cls = JSONEncoder  # 赋了一个新的东西
        return cls(...)   # JSONEncoder(),类名加括号--->实例化对象
3.打开源码,会发现类JSONEncoder()下面有对象实例化的__init__方法
4.主要研究JSONEncoder(),会发现它下面有一个default方法,它里面的内容是:
	raise TypeError(f'Object of type {o.__class__.__name__} '
                        f'is not JSON serializable')
  会发现跟我们上面报的错一样,raise是主动抛出异常,说明我们报错的原因就是由default触发的
5.想要它不报错的解决措施是:
	写一个类继承JSONEncoder(),然后在这个类里面也定义一个default方法,然后以后再走default的时候先走我们自己定义的default方法,然后在自己定义的default里可以写一些操作
	
6.小案例主要告诉我们在什么时候需要去考虑用派生,去继承类,重写里面的方法
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)


补充一个小知识:python解释器--->view--->appearance--->toolbar,找到toolbar并且勾选,可以后面用来返回上一次的页面。
'''

二、三大特性之封装

1.封装相关知识

1.封装其实就是将数据或者功能隐藏起来(包起来  装起来)

2.隐藏的目的不是让用户无法使用,而是给这些隐藏的数据'开设特定的接口' 让用户使用接口才可以去使用,我们在接口中'添加一些额外的操作' (换句话说就是找个中间商,赚个差价)

3.在'类的定义阶段'使用双下划线开头的名字,都是隐藏的属性,后续类和对象都无法直接获取
class Person:
    d_type = '人类'
    __school = '清华大学'
    def eat(self):
        print('今天是疯狂星期四,还好我不喜欢吃kfc,又省钱了')

obj = Person()
print(obj.__school)  # 无法访问
print(Person.__school)  # 无法访问
Person.__XXX = '信息学院'
print(Person.__XXX)  # 信息学院  是可以访问的

4.在python中不会真正的限制任何代码,隐藏的属性如果真的需要访问也可以,只不过需要做变形处理
	__变量名    _类名__变量名
class Person:
    d_type = '人类'
    __school = '清华大学'
    def eat(self):
        print('今天是疯狂星期四,还好我不喜欢吃kfc,又省钱了')

obj = Person()
print(Person.__dict__)

'''
{'__module__': '__main__', 
'd_type': '人类',
 '_Person__school': '清华大学',  # 名字变了形 ,可以通过,print(obj._Person__school)去访问,但是就失去了封装意义
 'eat': <function Person.eat at 0x0000024A5AD494C0>, 
 '__dict__': <attribute '__dict__' of 'Person' objects>, 
 '__weakref__': <attribute '__weakref__' of 'Person' objects>, 
 '__doc__': None,
  '__XXX': '信息学院'}
'''
ps:既然隐藏了 就不改使用变形之后的名字去访问 这样就失去了隐藏的意义

2.属性的封装

    
class Person:
    d_type = '人类'
    __school = '清华大学'
    __name = '张三'
    __age = 18

    def get_info(self):
        return f'''
        ------------------
        姓名:{self.__name}
        年龄:{self.__age}
        性别:{self.__school}
        ------------------
        '''

    def eat(self):
        print('今天是疯狂星期四,还好我不喜欢吃kfc,又省钱了')

obj = Person()
print(obj.get_info())
'''
        ------------------
        姓名:张三
        年龄:18
        性别:清华大学
        ------------------
'''
'''
封装的含义就是把一些东西隐藏掉,然后访问的时候不能直接访问,必须要通过提供的方法(封装接口),然后这个方法会返回数据,封装功能也是如此
'''

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)

stu1.set_info('lisa', 24)
stu1.set_info('','我很大')  # 用户不能为空
"""
我们编写python很多时候都是大家墨守成规的东西 不需要真正的限制  ‘君子协定’
class A:
	_school = '清华大学'
	def _choice_course(self):
		pass
ps:当别人看到你的名字前面加了一个_,那么他就会考虑这个是不能直接用的,他会考虑有没有接口供他使用。
	同样方法也是,他会考虑这个时候不能随便的调用了,只能在内部使用
"""

三、property伪装

1.了解封装

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

2.什么时候需要伪装?为什么要需要伪装?

 扩展知识:体质指数(BMI)=体重(kg)÷身高^2(m)
    
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('zhangran', 44, 1.60)
res = p1.BMI()
print(res)  # 17.187499999999996
'''
存在一个小问题:
    BMI应该是一个数据,不应该是一个功能,但是这个数据得需要通过计算才可以得到
怎么修改呢??
    class Person:
    def __init__(self, name, weight, height):
        # 实例化
        self.name = name
        self.weight = weight
        self.height = height

    # 这里是修改得部分,只需添加一直装饰器,但是不要有参数
    @@property
    def BMI():
        return self.weight / (self.height ** 2)

    p1 = Person('zhangran', 44, 1.60)
    # res = p1.BMI()
    # print(res)  # 17.187499999999996
    print(p1.BMI) # 17.187499999999996
'''
-----------------了解知识-------------------
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)
# obj.name = 666
# print(obj.name)
del obj.name
'''
伪装配套得俩个方法:@name.setter / @name.deleter
可以伪装的更彻底

'''

四、三大特性之多态

1.了解多态

多态:是指一种食物得多种形态
	例如:水:液态、固态、气态

2.多态的使用

class Animal(object):
    def spark(self):
        pass
class Cat(Animal):
    def miao(self):
        print('喵喵喵')
class Dog(Animal):
    def wang(self):
        print('汪汪汪')
class Pig(Animal):
    def heng(self):
        print('哼哼哼')

c1 = Cat()
d1 = Dog()
p1 = Pig()
c1.miao()  # 喵喵喵
d1.wang()  # 汪汪汪
p1.heng()  # 哼哼哼
'''
以上编写不符合多态性的概念,既然都是动物,那么叫叫的时候去调用同一个方法

一种事物有多种形态 但是相同的功能应该有相同的名字
这样的话 以后我无论拿到哪个具体的动物 都不需要管到底是谁 直接调用相同的功能即可
    无论你是鸡 鸭 猫 狗 猪 只要你想叫 你就调用固定的叫的功能
    
以下是多态性写法:
'''

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()  # 哼哼哼

3.老早之前我们其实就接触了多态

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))
'''
三者都是容器类型,在统计数据个数的时候都是调用的都是len
那么这个就是多态性的理论
'''

4.多态的了解知识

'''------------------了解知识-------------------'''
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()

5.多态之鸭子类型

鸭子类型
	其实我们完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象,这正是Python崇尚的“鸭子类型”(duck typing)
	只要你长得像鸭子 走路像鸭子 说话像鸭子 那么你就是鸭子
    
 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:一切皆对象
        只要你有数据 有功能 那么你就是对象
            文件名         文件对象
            模块名         模块对象       
"""

五、反射方法

1.反射的含义

	通过字符串来操作对象的数据方法

2.反射的四个主要方法

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

3.反射使用

class Student:
    school = '清华大学'

    def choice_course(self):
        print('选课')
stu = Student()

target_name = input('请输入你想要核查的名字>>>:')
print(hasattr(stu, target_name))
# hasattr,帮我们实现了变量名与字符串的转换

image-20220730124558699

class Student:
    school = '清华大学'

    def choice_course(self):
        print('选课')
stu = Student()
target_name = input('请输入你想要核查的名字>>>:')
print(getattr(stu, target_name))
'''
1.拿所给的字符串去所给的对象里面查找
2.查看是否存在一个变量名
3.如果存在的话就直接将它所绑定的值打印出来,如果是一个函数名那么加括号就可以使用了
4.如果输入一个不存在的名字,就会报错告诉我们,当前这个对象里面没有这个名字
'''

image-20220730125357524

# 需求:判断用户提供的名字在不在对象可以使用的范围内
# 方式一:利用异常处理
# try:
#     if stu.school:
#         print(f"True{stu.school}")
# except Exception:
#     print("没有属性")
# 这么写有缺陷,过于繁琐
'''
变量名school 与字符串school 区别大不大
    stu.school
    stu.'school'
两者虽然只差了引号 但是本质是完全不一样的 
'''
# 方式二:获取用户输入的名字 然后判断该名字对象有没有 上面的异常处理没有办法实现了 需要反射
while True:
    target_name = input('请输入你想要核查的名字>>>:')
    # 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('不好意思 您想要查找的名字 对象没有')

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()
# 用产生的对象去调用serve_forever()
obj.serve_forever()

image-20220730132000944

标签:__,name,self,多态,三大,print,property,class,def
来源: https://www.cnblogs.com/zxr1002/p/16534824.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有