Loading

类的封装-内置装饰器与反射

一、super进阶

在多继承中:严格按照mro顺序来执行

super是按照mro顺序来寻找当前类的下一类

在py3中不需要传参数,自动就帮我们寻找当前类的mro顺序的下一个类中的同名方法

在py2中的新式类中,需要我们主动传递参数super(子类的名字,子类的对象). 函数名()

这样才能够帮我们调用到这个子类的mro顺序的下一个类中的方法

在py2的经典类中,并不支持使用super来找下一个类

class A:
    def func(self):			
        print('is A')		


class B(A):
    def func(self):
        super().func()      
        print('is B')


class C(A):
    def func(self):
        super().func()      
        print('is C')


class D(B, C):
    def func(self):
        super().func()      
        print('is D')

d = D()
print(D.mro())
d.func()

# 输出
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
is A
is C
is B
is D

在单继承中:super就是找父类

class Foo:
    def func(self):
        print('is Foo')

class Son(Foo):
    def func(self):
        super().func()     # 先找父类的func方法--->打印is Foo
        print('is Son')             # 在打印自己的is Son


s = Son()
s.func()
# 输出
is Foo
is Son

二、封装

封装:就是把属性或方法装起来

广义:把属性和方法装起来,外面不能直接调用了,要通过类的名字来调用

狭义:把属性和方法藏起来,外面不能调用,只能在内部偷偷调用

所有的私有化都是为了让用户不在外部调用类中的某个名字

如果完成私有化,那么这个类的封装度就更高了,封装度越高各种属性和方法的安全性也越高,但代码越复杂

封装的语法:

1、给一个名字前面加上了双下滑线的时候,这个名字就变成了一个私有的

2、所有私有的内容或者名字都不能在类的外部调用,只能在类的内部使用了

# 不想让你看也不想让你改
class User:
    def __init__(self, name, pwd):
        self.name = name
        self.__pwd = pwd		# 私有的实例变量/私有的对象属性

use = User('admin', 'abc123')

print(use.__pwd)        # 报错
print(use.pwd)          # 报错

在外部查看和修改私有对象属性:

# 可以给你看也可以给你改,但你要用我自定义的方法看,和按照我的规则改
class User:
    def __init__(self, name, pwd):
        self.name = name
        self.__pwd = pwd

    def get_pwd(self):				# 表示用户不能改只能看
        return self.__pwd
    
    def change_pwd(self, count):	# 表示用户必须调用我们自定义的修改方式来进行变量的修改
        if count.isdigit():			# 是数字我就改,不是我就不改,还是原样
        	self.__pwd = count
        return self.__pwd

use = User('admin', 'abc123')

print(use.get_pwd())
use.change_pwd('123456789')		# 实际上是调用了对象内部的函数进行修改
print(use.get_pwd())
# 输出
abc123
123456789

1、加了双下划线的名字为啥不能从外部调用了?

class User:
    __Country = '中国'		 # 私有的静态变量
    __job = '攻城狮'			# 私有的静态变量

    def func(self):
        # 在类的内部使用的时候,自动的把当前这句话所在的类的名字拼在私有变量前完成变形(_User__Country)
        print(User.__Country)	# __Country已经变形成(_User__Country)

print(User.__dict__)
User().func()

'''
此时,变量做了一个转换所以在外部找不到了,如果外部调用转换后的变量还是可以调用的。(但不介意这样用)
__Country ---> _User__Country
__job     ---> _User__job
'''
# 输出
{..., '_User__Country': '中国', '_User__job': '攻城狮',...}
中国

2、私有的内容不能被子类使用。

class Foo:
    # __func:为私有的绑定方法
    def __func(self):		# 3、而父类Foo中的私有方法已经变形为_Foo__func了不是_Son__func方法了
        print('is Foo')

class Son(Foo):
    def __init__(self):
        self.__func()		# 2、子类__func方法变形成了_Son__func方法了,没有会去父类Foo找

Son()		# 1、实例化时,自动调用init方法

# 输出 报错
AttributeError: 'Son' object has no attribute '_Son__func'

3、在其他语言中的数据的级别都有哪些?在python中有哪些?

public 公有的 类内类外都能用,父类子类都能用 python支持

protect 保护的 类内能用,父类子类都能用,类外不能用 python不支持

private 私有的 本类的类内部能用,其他地方都不能用 python支持

三、内置的装饰器

property,setter,delter

property方法:把一个方法伪装成属性,在掉用这个方法的时候不需要加()就可以直接得到返回值。

方法一:

from math import pi

class Circle:
    def __init__(self, r):
        self.r = r

    @property		# 把area()函数伪装成属性
    def area(self):
        print(pi * self.r ** 2)


# 实例化一个半径为5的圆
y = Circle(5)
y.area		# 被伪装后圆的面积函数不用加()

# 输出
78.53981633974483

方法二:和私有的属性合作的

class User:
    def __init__(self, name, pwd):
        self.name = name
        self.__pwd = pwd

    @property
    def pwd(self):
        print(self.__pwd)


u = User('小杨', '1234')
u.pwd
# 输出
1234

setter方法:对伪装方法可以使用和属性一样的 = 号了

class User:
    def __init__(self, name, pwd):
        self.name = name
        self.__pwd = pwd

    @property
    def pwd(self):
        print(self.__pwd)

    @pwd.setter
    def pwd(self, count):
        print('我这里setter被调用了')
        if isinstance(count, int):	# 满足条件(count是int类型)我就修改
            self.__pwd = count



u = User('小杨', '1234')
u.pwd = 1111	# 可以使用 = 进行满足条件的赋值了,不满足原样不变
u.pwd

# 输出
我这里setter被调用了
1111

delter方法:(了解)

class User:
    def __init__(self, name, pwd):
        self.name = name
        self.__pwd = pwd

    @property
    def pwd(self):
        print(self.__pwd)

    @pwd.deleter
    def pwd(self):
        print('我这里delter被调用了')


u = User('小杨', '1234')

del u.pwd
# 输出
我这里delter被调用了

四、反射

getatter、hasatter

反射:用字符串类型的名字来操作这个名字对应的函数、实例变量、绑定方法、各种方法

在明明知道一个变量的字符串数据类型的名字,想直接调用它,但是调不到的时候,用反射

getattr方法:

1、反射对象的:实例变量、绑定方法

class Person:
    def __init__(self, name , age):
        self.name = name
        self.age = age

    def func(self):
        print('is func')

# 实例化一个对象
Bob = Person('鲍勃', 18)

print(getattr(Bob, 'name'))		# 反射对象的实例变量
getattr(Bob, 'func')()			# 反射对象的绑定方法

# 输出
鲍勃
is func

2、反射类的:静态变量

class Person:
    attribute = '会思考'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def func(self):
        print('is func')


print(getattr(Person, 'attribute'))     # 反射类的静态变量

# 输出
会思考

3、反射模块中的所有变量

反射被导入的模块:

# 模块 my_module.py 内容
count = '模块 my_module.py 中count的内容'

def func():
    return '这是 my_module 模块中的 func 函数'

# --------------------------------------------------------------

import my_module

ret = getattr(my_module, 'count')
ret2 = getattr(my_module, 'func')
print(ret)
print(ret2())
# 输出
模块 my_module.py 中count的内容
这是 my_module 模块中的 func 函数

4、反射当前执行的py文件 ——> 当前脚本

import sys

lis = [1, 2, 3]

def func():
    return 'is func'

ret1 = getattr(sys.modules['__main__'], 'lis')		# 反射当前脚本的变量
ret2 = getattr(sys.modules['__main__'], 'func')		# 反射当前脚本的函数
print(ret1)
print(ret2())
# 输出
[1, 2, 3]
is func

hasattr方法:

hasattr 方法其实是和 getattr 方法成对使用的,主要用来判断反射变量是否存在

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def func(self):
        print('is func')


Bob = Person('鲍勃', 18)
print(hasattr(Bob, 'name'))     # name 变量名存在就返回True
print(hasattr(Bob, 'sex'))      # sex 变量不存在就返回False

if hasattr(Bob, 'func'):                    # 判断 func 是否存在
	# 存在,判断 func 是否可以被调佣(用 callable 判断是否是函数)
    if callable(getattr(Bob, 'func')):      
        getattr(Bob, 'func')()              # 是函数就加括号()
    getattr(Bob, 'func')                    # 不是就不用加括号()

# 输出
True
False
is func

五、反射例子

反射例子一:

import sys

class WeChat:
    def __init__(self, username):
        self.username = username

    def pay(self, money):
        print(f'{self.username}通过微信充值了{money}')

class Alipay:
    def __init__(self, username):
        self.username = username

    def pay(self, money):
        print(f'{self.username}通过支付宝充值了{money}')


# 简化归一化设计:
def pay(username, money, kind):
    class_name = getattr(sys.modules['__main__'], kind)     # 输入的类名,反射本地的类
    obj = class_name(username)             # 用上一步反射的类来创建实例化对象
    obj.pay(money)

pay('小杨', 200, 'WeChat')

# 输出
小杨通过微信充值了200

反射例子二:

用户输入用户名密码性别
实例化对象
用户任意输入内容 :
不能用异常处理
如果输入的是属性名 打印属性值
如果输入的是方法名 调用方法
如果输入的什么都不是 不做操作

class User:
    def __init__(self, name, pwd, sex):
        self.name = name
        self.pwd = pwd
        self.sex = sex

    def eat(self):
        print(f'{self.name},在吃饭')

    def sleep(self):
        print(f'{self.name},在睡觉')


Bob = User('鲍勃', 18, '男')

count = input('需要操作的方法:')

if hasattr(Bob, count):
    if callable(getattr(Bob, count)):
        getattr(Bob, count)()
    print(getattr(Bob, count))

反射例子三:

循环这个列表
显示 序号 用户要做的操作
用户输入序号
你通过序号找到对应的login或者register方法
先实例化
调用对应的方法,完成登录或者注册功能

class Authentic:
    ll = [('登录', 'login'), ('注册', 'register')]

    def __init__(self, username, pwd):
        self.username = username
        self.pwd = pwd

    def register(self):
        print('注册成功:')

    def login(self):
        print('登录成功:')


Bob = Authentic('python', 123456)

for index, func in enumerate(Authentic.ll, 1):	
    print(index, func[0])

count = int(input('请选择需要操作的序号>>:'))
if hasattr(Bob, Authentic.ll[count - 1][1]):
    getattr(Bob, Authentic.ll[count - 1][1])()
# 输出
1 登录
2 注册
请选择需要操作的序号>>:2
注册成功:
posted @ 2021-05-04 21:27  Mr-Yang`  阅读(116)  评论(0编辑  收藏  举报