面向对象之封装,多态,反射
派生实际应用
import datetime
import json
import datetime
import json
class MyJsonEncoder(json.JSONEncoder):
def default(self, o):
# 形参o就是即将要被序列化的数据对象
# print('重写了', 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) # 调用父类的default(让父类的default方法继续执行 防止有其他额外操作)
d1 = {'t1': datetime.datetime.today(), 't2': datetime.date.today()}
res = json.dumps(d1, cls=MyJsonEncoder)
print(res)
"""
TypeError: Object of type 'datetime' is not JSON serializable
json不能序列化python所有的数据类型 只能是一些基本数据类型
json.JSONEncoder
1.手动将不能序列化的类型先转字符串
{'t1': str(datetime.datetime.today()), 't2': str(datetime.date.today())}
2.研究json源码并重写序列化方法
研究源码发现报错的方法叫default
raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__)
我们可以写一个类继承JSONEncoder然后重写default方法
"""
能够被序列化的数据是有限的>>>:必须是下列左边的类型
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
面向对象三大特性之封装
封装的含义:
就是将数据与功能隐藏起来,不能直接调用,必须使用单独开发的接口来调用这些隐藏起来的数据并利用该接口添加额外的操作。分为 封装与隐藏
1.封装: 就是将数据和功能封装起来。
2.隐藏: 将数据与功能隐藏起来,只能通过接口调用并增加额外的操作
类在定义阶段变量名前有两个下划线,就是隐藏该名字,功能也可以使用两个下划线来隐藏
我们可以直接通过'_Student__school' 与 '_Student__choice_course'这两个名字来访问,但是这样是不可取的,隐藏就是为了让你不要直接去访问他,你这样还有啥意思。自觉遵守。
"""要注意的是类只有在定义阶段才可以隐藏,如果你在下面使用 类名.隐藏名 修改数据 那么隐藏就无效了"""
对象也可以拥有隐藏属性
class Student(object):
school_name = '考上大学啦'
__school = '家里蹲大学'
def __init__(self, name, age):
self.__name = name
self.__age = age
# 开放修改接口,并增加一些特殊功能,
'如果年龄也需要修改,只需在形参里加上年龄'
def set_name(self,new_name):
if len(new_name)== 0: # 判断用户输入的名字是否为空
raise ValueError('你啥都不输什么意思')
if new_name.isdigit(): # 判断用户输的名字是否是数字
raise ValueError('让你输个名字你输数字搞毛啊')
self.__name = new_name
obj = Student('tank',18)
print(obj.__dict__) # {'_Student__name': 'tank', '_Student__age': 18}
obj.set_name('jason') # 利用接口修改隐藏名字的数据
print(obj.__dict__) # {'_Student__name': 'jason', '_Student__age': 18}
"我们在隐藏一些名字的同时,应该开放一个修改这些 名字的数据值 的接口,同时还能加上一些判断或别的"
伪装
@property 可以将方法伪装成数据
class Person(object):
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
@property
def BMI(self):
return '%s您的BMI指数是%s' % (self.name, self.weight / (self.height ** 2))
obj = Person('lxj',1.78,75)
obj.BMI() # 报错 此时方法已经被转成基本数据
print(obj.BMI) # lxj您的BMI指数是23.671253629592222
三大特性之多态
多态:一种事物的多种形态
eg:
动物:猪狗牛羊猫
class Animal(object):
def speak(self):
pass
class Chicken(Animal):
def speak(self):
print('咯咯咯')
class Duck(Animal):
def speak(self):
print('嘎嘎嘎')
class Sheep(Animal):
def speak(self):
print('咩咩咩')
"动物,我们只要提起动物那么第一时间就想起了动物们都会叫,那么他们就应该有一个共同的方法 叫"
C1 = Chicken() # 模拟鸡
D1 = Duck() # 模拟鸭
S1 = Sheep() # 模拟羊
C1.speak() # 咯咯咯
D1.speak() # 嘎嘎嘎
S1.speak() # 咩咩咩
"""面向对象中多态意思是 一种事物可以有多种形态但是针对相同的功能应该定义相同的方法
这样无论我们拿到的是哪个具体的动物 都可以通过相同的方法调用功能。
我们如何来寻找共同的特征的方法:
在python中称为:鸭子类型:只要你看上去像鸭子 走路像鸭子 说话像鸭子 那么你就是鸭子
"""
多态提供了强制约束子类的方法: 不推荐使用
import abc
# 指定metaclss 属性将设置为抽象类,抽象类本身只能用于约束子类
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 使用 该装饰器 现在子类必须要有 speak方法
def speak(self):
pass
class Chicken(Animal): # 只要继承Animal 必须要有 speak方法
def speak(self):
print('咯咯咯')
class Duck(Animal):
def jiao(self): # 没有speak方法
print('嘎嘎嘎')
D1 = Duck() # 直接报错 TypeError: Can't instantiate abstract class Duck with abstract methods speak
"""
在linux系统中有一句话>>>:一切皆文件!!!
内存可以存取数据
硬盘可以存取数据
那么多有人都是文件
"""
class Memory(object): # 内存
def read(self):
pass
def write(self):
pass
class Disk(object): # 磁盘
def read(self):
pass
def write(self):
pass
# 得到内存或者硬盘对象之后 只要想读取数据就调用read 想写入数据就调用write 不需要考虑具体的对象是谁
三大特性之反射
利用字符串操作对象的数据和方法
1.hasattr()
'判断对象是否含有某个字符串对应的属性名或方法名'
2.getattr()
'根据字符串获取对象对应的属性名(属性的值)或方法名(函数名及代码)'
3.setattr()
根据字符串给对象设置或修改数据
4.delattr()
根据字符串删除对象里面的名字
在于用户交互的情况下,我们得到用户输入的值都是字符串,无法通过字符串来直接找到该名字。此时就有了反射
class A:
name = 'tank'
def get(self):
pass
obj = A()
target_name = input('请输入你想要对象使用的名字').strip()
hasattr()
print(hasattr(obj,target_name)) # 用户输入name 得到True
print(hasattr(obj,target_name)) # 用户输入get True
print(hasattr(obj,target_name)) # 用户输入age 此时类的名称空间内并没有这个名字False
getattr()
print(getattr(obj,target_name)) # 用户输入name 得到 值 tank
面向对象的魔法方法
类中定义的双下方法都称为魔法方法.
在特定的条件下自动触发运行,不需要人去调用
__init__方法
'对象添加独有数据时'自动触发
class A:
def __init__(self,name):
self.name = name
obj = A('tank') # 等同于 obj.name = tank
"在我们生成对象时,传值的时候就等于self.name = 传的值 此时就会自动触发"
__str__方法
'对象被执行打印操作时'自动触发
class A:
def __init__(self,name):
self.name = name
def __str__(self):
return '今天是个好日子'
obj = A('tank')
print(obj) # 今天是个好日子
"返回的是字符串。如果返回字符串以外的数据类型则直接报错"
__call__方法
'对象加括号调用'就会自动触发
class A:
def __init__(self,name):
self.name = name
def __call__(self, *args, **kwargs):
print('我触发了哦',args,kwargs)
return '起飞'
obj = A('tank')
print(obj(111))
'''我触发了哦 (111,) {}
起飞
'''
args 是个空元组用来接收参数
kwargs 是个空列表 用来接收关键字传参
__getattr__方法
对象 '点'不存在的'名字'的时候自动触发
class A:
def __init__(self,name):
self.name = name
def __getattr__(self, item):
print('item是不存在的名字:',item)
obj = A('tank')
print(obj.age) # item是不存在的名字: age
'在正常情况下点一个不存在的名字会报错'
__getattribute__方法
对象点名字就会自动触发,有它在就不会触发
class A:
def __init__(self,name):
self.name = name
def __getattr__(self, item):
print('我是你大哥')
def __getattribute__(self, item):
print('有我在就不会执行双下__getattr__')
obj = A('tank')
print(obj.age) # 有我在就不会执行双下__getattr__
__setattr__方法
给对象添加或者修改数据的时候自动触发 对象.名字 = 值
class A:
def __init__(self, name):
self.name = name
def __setattr__(self, key, value):
print('执行setattr:', key, value)
obj = A('tank') # 执行setattr: name tank
obj.age = 18 # 执行setattr: age 18
enter 方法
__exit__方法
__enter__当对象被当做with上下文管理操作的开始自动结束。并且返回什么 as后面的变量名就会接收到什么
__exit__ with上下文管理语法运行完毕之后自动触发(子代码结束)
class A:
def __init__(self, name):
self.name = name
def __enter__(self):
print('开始时执行我并接收我的返回值')
return '哈哈哈'
def __exit__(self, exc_type, exc_val, exc_tb):
print('子代码结束时执行')
obj = A('tank')
with obj as f:
print(f)
============================
开始时执行我并接收我的返回值
哈哈哈
子代码结束时执行
============================
魔法方法之_new_
"""
双下new 用来产生空对象
双下init 用来实例化对象
"""
class MyMetaClass(type):
def __call__(self, *args, **kwargs):
obj = self.__new__(self) # 产生一个空对象(骨架) 这里的括号内(self)其实是类
self.__init__(obj,*args, **kwargs) # 调用__init__给对象添加独有数据(血肉)
# 返回创建好的对象
return obj
class Student(metaclass=MyMetaClass):
def __init__(self,name):
self.name = name
obj = Student('tank')
print(obj.__dict__)
元类简介
元类——产生类的类
我们之前查看数据类型都是用 type 方法查看,我们再看结果的时候看到前面的class不知道是什么 ,学过面向对象 后 class 这不就是类吗
使用type方法查看到 对象属于产生该对象的类
"""所以我们得到结论 type 就是所有类的元类 """
创建类的两种方式
第一种 使用 关键字 class
class B(object):
pass
print(B)
# <class '__main__.B'>
第二种 type元类
我们通过查看type的源码查看到方法
'type(name, bases, dict) -> a new type'
type(类名,父类,名称空间)
元类定制类的产生行为
"""元类 能够控制类的创建,那么我们就可以在创建过程中添加各种额外操作"""
使用元类控制 产生所有的 类的时候 类名首字母必须是大写
我们在生产类的时候会自动触发元类的__init__方法来创建类,那么我们可以自定义一个元类,重写init方法。
1.元类定制类的产生行为:
class MyMetaClass(type): # what 是类名, bases父类, dict 名称空间
def __init__(cls, what, bases=None, dict=None): # 重写init方法,会飘黄,因为正在修改元类的方法
if not what.istitle(): # 添加判断
raise Exception('类名首字母大写哦') #
super().__init__(what,bases,dict)
# 指定类的元类 必须使用关键字 metaclass
class myclass(metaclass=MyMetaClass):
pass # Exception: 类名首字母大写哦
标签:总结,__,obj,name,self,class,def
From: https://www.cnblogs.com/LiaJi/p/16890093.html