面向对象(三大特性封装与多态)
目录
注:上一篇了解了比较常用的面向对象的继承特性
一.面向对象三特特性之封装
# 1.什么是封装:是将类中某些'名字'隐藏起来不让外界直接调用。
# 2.封装的目的:是为了提供专门的通道进行访问.并可以在通道内添加额外的功能。
# 3.如何封装名字:在需要隐藏的变量名前加上两个下划线'__'。
# 4.封装的功能只能在类定义阶段才生效。
# 5.如果看到定义类中双下变量名,就表示这个属性需要通过特定方法(通道,接口)去访问
1.例子
class Student(object):
school = '家里蹲大学'
__label = '勤学苦练'
def __init__(self, name, age):
self.name = name
self.age = age
def choose_course(self):
print('%s选择' % self.name)
p1 = Student('thn', 25) # 传值给init
# print(p1.school) # 通过对象找到类包含的公共属性
# print(p1.name) # 通过对象找到name
# print(p1.age) # 通过对象找age
# print(p1.__dict__) # 通过对象查询字典
# print(Student.school) # 通过类找类内公共属性
# print(p1._Student__label) # 通过对象_Student查找到隐藏(封装属性)不推荐
# print(Student._Student__label) # 通过类_Student查找到隐藏(封装属性) 不推荐
2.例子tow
class Student(object):
__slots = '家里躺大学'
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
# p1 = Student('thn', 25)
# p1.check_info()
# print(Student.__dict__)
# print(p1.__dict__)
2.1总结
"""
1.封装双下方法:将数据隐藏起来限制数据,被外部直接操作
2.内部应提供对应接口允许外部直接操作数据
"""
# 3.目的:隔离复杂度
二.将方法伪装成数据
"""property"""
# 1.数据需要经过计算才能获得
# 2.BMI指数:应该是属于人体的数据而不是人体的功能
# 3.能将功能伪装成数据名@property @classmethod @staticmthod
1.体质计算
"""体质指数(BMI)=体重(kg)÷身高^2(m)"""
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))
三.面向对象三大特性之多态
# 1. 什么是多态:指的是事物的多种形态
例如:动物:老虎,狮子,狼,大象
# 2.多态性:就是例如所讲的每个'动物独有的特性,习性,生活方式等'
# 3.多态性优点:增加了程序的灵活性与可扩展性。
# 4.鸭子理论:
"""你看着像鸭子,走路像鸭子,说话像鸭子,那么你就是鸭子"""
四.面向对象之反射
# 1.什么是反射:通过字符串来操作对象的数据和功能
# 2.反射的四种方法:
hasattr():判断对象是否含有字符串对应的数据或者功能
getattr():根据字符串获取对应的变量名或者函数名
setattr():根据字符串给对象设置键值对(名称空间中的名字)
delattr():根据字符串删除对象对应的键值对(名称空间中的名字)
"""
什么时候使用反射:关键字(对象和字符串,用户输入,用户自定义,指定)
还有关键在于:字符串与变量名是有本质区别的
"""
1.反射例子
class WinCmd(object):
def ls(self):
print('windows执行ls命令')
def dir(self):
print('windows执行dir指令')
def cd(self):
print('windows执行cd指令')
class LinuxCmd(object):
def ls(self):
print('linux执行ls命令')
def dir(self):
print('linux执行dir指令')
def cd(self):
print('linux执行cd指令')
p1 = WinCmd()
p2 = LinuxCmd()
def run(p1):
while True:
cmd = input('请输入您要执行的指令>>:').strip()
if hasattr(p1,cmd):
func_name = getattr(p1, cmd)
func_name()
else:
print('cmd command not found')
run(p1)
# 执行的是windows命令用过cun函数里的反射判断执行对应的指令
run(p2)
# 执行的是linux命令用过cun函数里的反射判断执行对应的指令
五.面向对象里的双下方法
# 1.__init__: 对象实例化自动触发,不需要调用
# 2.__str__: 对象被执行打印和操作时候自动触发,返回的是字符串类型
可以返回更加精准的数据(对对象的描述) 变量名=类(对象)print对象
"""总结(作用:就是通过返回reture 字符串您需要对象打印出的数据信息
而不是打印出对象的内存地址)"""
# 3.__del__: 对象被执行(主动|被动)删除操作自动触发)
# 3.1 del 关键字 主动删除
# 4.__getattr__: 对象查找不存在名字的时候自动触发,没有返回None
item自动转换为字符串
# 5.__setattr__:对象在执行赋值操作的时候自动触发,并且还可以对对象进行额外的操作(判断等限制)
# 6.__call__: 对象被加括号的情况下自动触发,可以让对象也拥有加括号调用的功能(*args,**kwargs)元组,字典
# 7.__enter__: 对象被执行wiht文件管理语法之后自动触发,该方法返回什么as
# 8.__exit__: 对象被执行with文件语法结束后自动触发
# 9.__getattribute__: 对象查找名字无论存在都会执行该方法,如果类中
__getattribute__和__getattr__都纯在那么就不会执行__getattr__方法
1.例子
"""让字典通过句点符去查找值的功能"""
class MyDict(dict): # 类对应的一个空字典
def __getattr__(self, item): # 查找名字不存在触发
return self.get(item)
# self接收了对象 item里面是字典的值 通过return.对象get出字典的数据
# 实现了通过可以用.的方式取出字典里的值
# 要区别是名称空间的名字还是数据k:v键值对
obj = MyDict({'name':'jason','age':18})
print(obj.name) jason
print(obj.age) 18
class MyDict(dict):
def __setattr__(self, key, value): # 通过方法添加属性
self[key] = value # 符合K:V添加键值对
obj = MyDict({'name':'jason','age':18})
obj.password = 123
print(obj)
# {'name':'jason','age': 18, 'password': 123}
2.例子
"""补全代码运行时不报错"""
class Context:
def __enter__(self): # 通过方法触发
return self # self 包含的就是as后面的就是ctx
def __exit__(self, exc_type, exc_val, exc_tb):
pass
def do_something(self): # __neter__与__exit__两兄弟
pass
with Context() as ctx:
ctx.do_something() # 通过ctx.do_something():其实运行
六.元类
# 1.什么是元类:产生类 的类
class MyClass(object):
pass
obj = MyClass()
print(type(MyClass))
# <class 'type'>
# 2.class产生了类Myclass
# 3.type其实就所有类默认的元类
七.类的两种形式
# 1.class 关键字 负责产生类
class MyClass(object):
pass
print(MyClass)
# <class '__main__.C1'>
# 2.type type(类名,父类,类的名称空间)
res = type('MyClass', (), {})
print(res)
# <class '__main__.C1'>
--------------------------------------------------------------
元类的目的:
"""元类可以控制类的创建,当然可以可以修改"""
八.元类的基本使用
"""
1.元类是不能通过继承的方式直接指定修改的
2.需要通过关键字參数的形式进行修改
"""
class MyTypeClass(type):
def __init__(cls, cls_name, cls_bases, cls_dict):
# print(cls, cls_name, cls_bases, cls_dict)
if not cls_name.istitle():
raise Exception("创建类开头必须为大写")
super().__init__(cls_name, cls_bases, cls_dict)
class C1(metaclass=MyTypeClass):
pass
class C2(metaclass=MyTypeClass):
pass
class q(metaclass=MyTypeClass):
pass
# 这个会报错并出现打印if判断后的结果
九.元类进阶操作
# 对象加括号会自动执行产生该对象的类里面的__call__, 并且该方法返回什么对象加括号就会得到什么
# 类加括号会执行元类的里面的__call__该方法返回什么其实类加括号就会得到什么
"""类里面的__init__方法和元类里面的__call__方法执行的先后顺序"""
class MyTypeClass(type):
def __call__(self, *args, **kwargs):
# print('cun')
super().__call__(*args, **kwargs)
class MyClass(metaclass=MyTypeClass):
def __init__(self, name):
print('cun1')
self.name = name
obj = MyClass('jason')
# 首先执行 __call__ 下的cun打印
# 如果不从新执行super.()__call__ 那么不会执行cun1
# 其次打印 __init__ 下的cun1打印
-------------------------------------------------
"""如果要查找名字的话"""
class MyTypeClass(type):
def __call__(self, *args, **kwargs):
print(args, kwargs) #__call__接受了对象里的jason
super().__call__(*args, **kwargs)
class MyClass(metaclass=MyTypeClass):
def __init__(self, name):
self.name = name
obj = MyClass('jason')
# ('jason'){}
-------------------------------------------------
class MyTypeClass(type):
def __call__(self, *args, **kwargs):
if args: # 接受判断是否有值
raise Exception('必须全部采用关键字参数') # 这个其实可以任意修改报错的类别就是关键字参数
super().__call__(*args, **kwargs)
class MyClass(metaclass=MyTypeClass):
def __init__(self, name,age):
self.name = name # 关键字 参数
self.age = age # 关键字 参数
"""强制规定:类在实例化产生对象的时候 对象的独有数据必须采用关键字参数"""
obj1 = MyClass('jason')
obj2 = MyClass(name = 'jason',age = 18)
"""
如果你想高度定制类的产生过程
那么编写元类里面的__init__方法
如果你想高度定制对象的产生过程
那么编写元类里面的__call__方法
"""
__new__用于产生空对象(类) 骨架
__init__用于实例化对象(类) 血肉
"""
注意:并不是所有的地方都可以直接调用__new__ 该方法过于底层
如果是在元类的__new__里面 可以直接调用
class Meta(type):
def __new__(cls, *args, **kwargs):
obj = type.__new__(cls,*args,**kwargs)
return obj
如果是在元类的__call__里面 需要间接调用
class Mate(type):
def __call__(self, *args, **kwargs):
obj = object.__new__(self) # 创建一个空对象
self.__init__(obj,*args,**kwargs) # 让对象去初始化
return obj
"""