继承下的派生实际应用
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方法
"""
面向对象-------封装
简介
封装
1.封装是面向对象编程的一大特点
2.面向对象编程的第一步 将属性和方法封装到一个抽象的类中
3.外界使用类创建对象,然后让对象调用方法
4.对象方法的细节都被封装在类的内部
说简单的就是隐藏对象的属性和实现细节,仅对外提供公共访问方式。
基础入门 —> setter、getter
'''
封装就是指隐藏对象中一些不希望外部所访问到的属性或方法,即为了保证安全
如何封装,或者说如何隐藏属性或方法?
在定义类时,将属性或方法名修改为外部并不知道的名称,如下定义类
'''
class dog:
def __init__(self,name,age):
self.name = name
self.age = age
def hello(self):
print(f'大家好,我是{self.name}')
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
#直接可以修改类中的属性,不安全
d.name = '金毛'
d.hello()
执行结果
大家好,我是哈士奇
大家好,我是金毛
'''
那么我们可以这样定义,即内部使用hidden_name来定义属性,外部就不能直接通过name来修改访问类中的属性了
'''
class dog:
def __init__(self,name,age):
self.new_name = name
self.new_age = age
def hello(self):
print(f'大家好,我是{self.new_name}')
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
#外部不可以修改类中的属性
d.name = '金毛'
d.hello()
执行结果
大家好,我是哈士奇
大家好,我是哈士奇
'''
这样外部虽然不能通过name来修改属性值,但是还是可以通过new_name来修改属性值,当然我们是站在上帝的视角来看待这个问题,对于实例化类的对象而言,其并不是完全知道类中的属性与方法
那么,此时对象如何知道和修改属性的值呢?
很简单,在类中定义用于修改和获取属性的方法 set_name、get_name
需要提供一个getter和setter方法使外部可以访问到属性
getter 获取对象中的指定属性(get_属性名)
setter 用来设置对象的指定属性(set_属性名)
'''
且看如下代码
class dog:
def __init__(self,name,age):
self.new_name = name
self.new_age = age
def hello(self):
print(f'大家好,我是{self.new_name}')
#用于实例化对象设置修改类属性的方法
def set_name(self,name):
self.new_name = name
#用于实例化对象获取得到类属性的方法
def get_name(self):
return self.new_name
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
#外部使用get_name()方法来修改类中的属性
d.get_name()
#外部使用set_name()方法来修改类中的属性
d.set_name('金毛')
d.hello()
'''
这样做有什么好处呢?
使用封装,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性
'''
1.隐藏了属性名,使调用者无法随意的修改对象中的属性
2.增加了getter和setter方法,很好的控制属性是否是只读的
如果希望属性是只读的,则可以直接去掉setter方法
如果希望属性不能被外部访问,则可以直接去掉getter方法
3.使用setter方法设置属性,可以增加数据的验证,确保数据的值是正确的
在方法中判断年龄等属性的数值是否满足正常范围 >0
4.使用getter方法获取属性,使用setter方法设置属性;可以在读取属性和修改属性的同时做一些其他的
处理
进阶使用 —> 私有属性
以上只是根据在面向对象中对封装的简单概述,但是在真实环境中用的不是以上的这些方式,用的是Python
中自动的处理封装的属性,如下所示
可以为对象的属性使用双下划线开头,__xxx(属性名)
双下划线开头的属性,是对象的隐藏属性,隐藏属性只能在类的内部访问,无法通过对象访问
其实隐藏属性只不过是Python自动为属性改了一个名字
实际上是将名字修改为了,_类名__属性名 比如 __name -> _Person__name,这样还是可以访问的
防君子不防小人(并不是绝对不能访问,如果一定要访问还是可以通过对象名._类名__属性名来访问)
class dog:
def __init__(self,name,age):
self.__name = name
self.__age = age
def hello(self):
print(f'大家好,我是{self.__name},我{self.__age}岁了')
#用于实例化对象设置修改类属性的方法
def set_name(self,name):
self.__name = name
#用于实例化对象获取得到类属性的方法
def get_name(self):
return self.__name
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
print(d._dog__name)
# 其实可以通过如下方式修改私有属性的值,但是不会这么做,因为外部是不可以修改私有属性的值的
d._dog__name = '金毛'
d._dog__age = '4'
d.hello()
执行结果
大家好,我是哈士奇,我3岁了
哈士奇
大家好,我是金毛,我4岁了
强化加强 —> property
property装饰器,用来将一个get方法,转换为对象的属性
添加为property装饰器以后,我们就可以像调用属性一样使用get方法
使用属性的方式去调用方法,看上去是对属性赋值,但是本质上是调用方法的
使用property装饰的方法,必须和属性名是一样的
设置setter前必须设置getter
class dog:
def __init__(self,name,age):
self._name = name
self._age = age
def hello(self):
print(f'大家好,我是{self._name},我{self._age}岁了')
@property
def name(self):
print('get方法执行了~~~')
return self._name
# setter方法的装饰器:@属性名.setter
@name.setter
def name(self,name):
print('set方法执行了~~~')
self._name = name
@property
def age(self):
print('get方法执行了~~~')
return self._age
# setter方法的装饰器:@属性名.setter
@age.setter
def age(self,age):
print('set方法执行了~~~')
self._age = age
#实例化一个对象
d = dog('哈士奇',3)
d.hello()
print('--'*30)
# 调用set方法,修改name的值;注意看上去是修改属性名的方式,其实是调用的了方法,注意看输出
d.name = '德牧'
d.hello()
print('--'*30)
d.age = 4
d.hello()
print('--'*30)
print(d.name)
# 执行结果
# 大家好,我是哈士奇,我3岁了
# ------------------------------------------------------------
# set方法执行了~~~
# 大家好,我是德牧,我3岁了
# ------------------------------------------------------------
# set方法执行了~~~
# 大家好,我是德牧,我4岁了
# ------------------------------------------------------------
# get方法执行了~~~
# 德牧
面向对象-------多态
多态顾名思义多种状态,在python中,不同的对象调用同一个接口,表现出不同的状态,称为多态。
要实现多态有两个前提:
1.继承:多态必须发生在父类与子类之间
2.重写:子类重写父类方法
例:
class Animal():
def who(self):
print("I am an Animal")
class Duck(Animal):
def who(self):
print("I am a duck")
class Dog(Animal):
def who(self):
print("I am a dog")
class Cat(Animal):
def who(self):
print("I am a cat")
if __name__ == "__main__":
duck=Duck()
dog=Dog()
cat=Cat()
duck.who()
dog.who()
cat.who()
'''多态的使用增强了程序的灵活性和可扩展性'''
反射
hasattr():判断对象是否含有字符串对应的数据或者功能
getattr():根据字符串获取对应的变量名或者函数名
setattr():根据字符串给对象设置键值对(名称空间中的名字)
delattr():根据字符串删除对象对应的键值对(名称空间中的名字)
class Hero(object):
def __init__(self,name,blood):
self.name = name
self.blood = blood
@property
def get_name(self):
return self.name
@get_name.setter
def set_name(self,name):
self.name = name
hero = Hero('卡莎',1000)
1.hasattr(object,name)
判断object中有没有name字符串对应的方法和属性,name可以是函数也可以是参数,还可以是变量,返回结
果为True、False
print(hasattr(hero,'name')) # True
2.getattr(object,name,default=None)
获取name属性,如果没有会报错,但是添加default属性后,没有name会直接打印default值
print(getattr(hero,'get_name')) # 卡莎
print(getattr(hero,'f1')) # <bound method Hero.f1 of <__main__.Hero object at 0x01E6D1C0>>
3.setattr(object,属性名,属性值)
返回值是None
设置属性对象的属性,还可以直接修改当前对象的属性
print(setattr(hero,'name','女警')) # None
print(hero.name) # 女警
4.delattr(object,属性名)
删除对象属性
print(hero.__dict__) # {'name': '卡莎', 'blood': 1000}
delattr(hero,'name')
print(hero.__dict__) # {'blood': 1000}