面向对象(三大特性封装与多态)


注:上一篇了解了比较常用的面向对象的继承特性

一.面向对象三特特性之封装

# 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方法
__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
"""
posted @ 2022-04-11 21:55  笑舞狂歌  阅读(39)  评论(0编辑  收藏  举报