Python笔记【八】

本文为博主原创,未经许可严禁转载。
本文链接:https://blog.csdn.net/zyooooxie/article/details/108095932

Python笔记的博客 很久很久很久没有分享过了,一是没有时间,二是懒。
最近需求不算太多,我也就继续学习、分享一篇啊。

个人博客:https://blog.csdn.net/zyooooxie

作用域

https://docs.python.org/zh-cn/3.9/tutorial/classes.html#python-scopes-and-namespaces

"""
@blog: https://blog.csdn.net/zyooooxie
"""

def test_1():
    """
    作用域
    :return:
    """

    # 一个作用域是 一个命名空间可直接访问的Python程序的文本区域。
    # 【作用域是针对变量而言,指申明的变量 在程序里的可应用范围,或者称为 变量的生效范围。】

    # 虽然作用域是静态地确定的,但它们会被动态地使用。

    # python解释器动态执行过程中,对遇到的变量进行解释时,是按照一条固定的作用域链 查找解释的,又称为LEGB法则。
    # • Local(最内部函数)局部作用域
    # • Enclosing(嵌套函数的外层函数)嵌套作用域
    # • Global(模块全局)全局作用域
    # • Built-in(内置)内置作用域

    # 我的理解:
    # • 嵌套函数的 内层函数的 作用范围;
    # • 嵌套函数的 外层函数的 作用范围;
    # • 全局作用域是文件级别的,或者说是模块级别的,每个py文件中处于顶层的变量 都是全局作用域范围内的变量;
    # • 内置作用域是预先定义好的,在__builtins__模块中。这些名称主要是一些关键字,例如open、range、quit等;

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

    # 上面4个作用域的范围排序是按照从内到外,从小到大排序的。

    # 内层范围可以引用外层范围的变量,外层范围不包括内层范围的变量。
    # 在局部作用域中,可以看到局部作用域、嵌套作用域、全局作用域、内建作用域中 所有定义的变量。

    pass

# TODO 强烈建议:使用IDE的debug功能
# TODO 强烈建议:多打断点 + 在Watches面板 观察x 、open 、 y


x = 11                  # Global


def f():
    # 如果一个名称被声明为全局变量,则所有引用和赋值将直接指向包含该模块的全局名称的中间作用域。
    # 要重新绑定在最内层作用域以外找到的变量,可以使用nonlocal语句声明为非本地变量。如果没有被声明为非本地变量,这些变量将是只读的(尝试写入这样的变量只会在最内层作用域中创建一个新的局
    # 部变量,而同名的外部变量保持不变)。

    open = 90               # Enclosing
    y = 8

    # 如果函数内部重新赋值了一个和全局变量名称相同的变量,则这个变量是本地变量
    x = 33

    print("f1:", x, 'x的值')          # 33
    print("f1:", open, 'open的值')   # 90
    print("f1:", y, 'y的值')          # 8

    def g():
        open = 40                   # Local

        print("g:", open, 'open的值')

        print("g:", y, 'y的值')       # Enclosing
        print("g:", x, 'x的值')
        print('g() 执行结束')

    g()

    print("f2:", x)         # 33
    print("f2:", open)      # 90
    print("f2:", y)         # 8

    out()


def out():
    # 如果在函数内部引用了一个和全局变量同名的变量,且没有重新赋值,那么此变量 引用的是全局变量。
    print('out:', x, 'x的值')             # Global

    y = 1111
    print('out:', y, 'y的值')

    open = 0.1111
    print('out:', open, 'open的值')

    print('out() 执行结束')


if __name__ == '__main__':
    pass

    print("main: f()执行前", x, 'x的值')    # 11

    f()

    print("main: f()执行后", x, 'x的值')    # 11
    print('main', open)         # <built-in function open>

在这里插入图片描述

global()、locals()

https://docs.python.org/zh-cn/3.9/library/functions.html#globals

https://docs.python.org/zh-cn/3.9/library/functions.html#locals

"""
@blog: https://blog.csdn.net/zyooooxie
"""

# locals() 实际上没有返回局部名字空间,它返回的是一个拷贝。所以对它进行修改,修改的是拷贝,而对实际的局部名字空间中的变量值并无影响。
# globals() 返回的是实际的全局名字空间,对 globals 所返回的字典的 任何的改动都会直接影响到全局变量的取值。


name = 'zyooooxie'
web = 'csdn'

print('在模块层级上,locals() 和 globals() 是同一个字典。')
# print(locals(), '1-locals')
# print(globals(), '1-globals')


def test_globals():
    a = globals()       # 这永远是当前模块的字典(在一个函数或方法内部时,这是指定义该函数或方法的模块,而不是调用该函数或方法的模块)
    print(a)

    # 2种方式 修改全局变量
    a['name'] = 'new_zyooooxie'
    print(globals())                # 修改成功

    global web
    web = 'new_csdn'
    print(globals())


def test_locals():
    # locals函数以字典的形式返回当前所在作用域的全部变量,
    # 如果你在一个模块里执行locals函数,那么它返回的与globals函数返回值相同;
    # 如果你在一个函数中执行locals函数,就只能返回这个函数所形成的局部作用域里的变量。
    print(locals())

    a = 1
    print(locals(), 'a')

    b = 2
    print(locals(), 'b')

    locals()['a'] = 11111               # 注意:更改 不会影响解释器使用的局部变量或自由变量的值。
    print(locals(), '直接修改a,实际locals()没变')


def test_locals2(int1: int, str1: str, list1: list, **kwargs) -> str:
    """
    locals() 的一些用法
    :param int1: 
    :param str1: 
    :param list1: 
    :param kwargs: 
    :return: 
    """
	
    print(locals(), '函数内部的变量')

    res = '第一,其中传入的实参值有 {}, {}'.format(int1, kwargs)
    print(res)

    res = '第二,其中传入的实参值有 {list1}, {kwargs}'.format(**locals())
    print(res)

    res = '第三,其中传入的实参值有 {kwargs}, 有 {0}, 有 {list1}, 有 {1}'.format(locals()['kwargs'], str1, **locals())
    print(res, '特别留意!!!')
    print('有 {kwargs}'.format(**locals()))
    print('有 {1}'.format(locals()['kwargs'], str1))

    res = f'第四,其中传入的实参值 有{kwargs}, 有{str1}'
    print(res)

    return res


if __name__ == '__main__':
    pass

    # test_globals()
    # test_locals()
    test_locals2(1, 'zyooooxie2', [3, 4.00], csdn55=('qq', '153132336'), zy6666='77777.77', xie8888='99999.99')

global语句、nonlocal语句

https://docs.python.org/zh-cn/3.9/reference/simple_stmts.html#the-global-statement

https://docs.python.org/zh-cn/3.9/reference/simple_stmts.html#the-nonlocal-statement

"""
@blog: https://blog.csdn.net/zyooooxie
"""

def test_2():
    """
    global、nonlocal语句
    :return:
    """
    # 注意:在内部函数中不能先访问外部函数变量,之后再定义一个同名的局部变量,这样会报错。
	# 函数内部只要是赋值操作 就表示声明为本地变量
	
    # --------------

    # 在一个模块内定义的函数的全局作用域就是该模块的命名空间,无论该函数从什么地方或以什么别名被调用。
    # 如果不存在生效的global或nonlocal语句,则对名称的赋值总是会进入最内层作用域。

    # def内部声明(赋值)的变量默认是本地变量,要想让其变成全局变量,需要使用global。
    # def内部如果没有声明某变量,则引用的这个变量是全局变量。

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

    # global 语句可被用来表明特定变量生存于 全局作用域 并且应当在其中被重新绑定;
    # nonlocal 语句表明特定变量生存于 外层作用域 并且应当在其中被重新绑定。

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

    # global:如果一个命名被声明为global,那么对于这个命名的所有的引用与赋值都会作用于模块的全局命名。
    # 【可以在函数内声明全局变量,然后实现对全局变量的修改。】

    # global可以声明一个或多个变量为全局变量,多个变量使用逗号隔开,也可以声明事先不存在的变量为全局变量。

    # 不能global中直接赋值。

    # 在 global 语句中列出的名称 不得在同一代码块内 该global语句之前的位置中使用【若使用的话,就将它声明为本地变量了】

    # 在global语句声明后,某些变量变为全局变量,实际是 未赋值。【必须先声明 后使用】

    # TODO 如果允许,应尽量不要将函数内部的变量修饰为全局变量

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

    # 在嵌套函数中,如果你想要在内层函数中使用 外层函数中的变量,是只可读 不可修改。

    # 在内层函数 尝试修改外层函数的变量,直接赋值时 会在内层函数 重新创建一个新的局部变量,而外层函数的变量依然存在。

    # nonlocal:它是用来重新绑定中间层次作用域中的变量。【在嵌套函数中,在内层函数声明nonlocal,可以实现修改外层函数的局部变量值。】

    # nonlocal 语句会使得所列出的名称 指向之前在最近的包含作用域中绑定的 除全局变量以外 的变量。
    # 【nonlocal默认将内层函数中的变量修饰为上一层函数的作用域范围,如果上一层函数中不存在该变量,则修饰为上上层、上上上层直到顶层函数,但不能修饰为全局作用域范围。】

    # nonlocal语句 修饰多个变量的时候,需要逗号隔开。

    pass

var = "123"

def scope_test():
    # print('1', globals())
    print('1', locals())

    def do():
        print('do()内部 var:', var, ';', 'var2:', var2)
        print('do()', locals(), '未重新赋值时,引用得是 外层函数中的变量')

    def do_2():
        # print(var)              # UnboundLocalError: local variable 'var' referenced before assignment

        var = "do_2新赋值的var"

        # print('2', globals())
        print('2', locals(), '只一个新定义的局部变量')

    def do_nonlocal():
        # print(var)              # SyntaxError: name 'var' is used prior to nonlocal declaration

        nonlocal var            # 没有在函数do_nonlocal()的域中创建一个变量,而是去引用到了外层的 【下面 var = "test-var"】
        nonlocal var2           # 去引用 外层的【下面 var2 = 'test-var2'】
        var = 'new-var'
        var2 = 'new-var2'

        new_var222222 = '用完就销毁'
        # nonlocal new_var222222    # SyntaxError: name 'new_var222222' is assigned to before nonlocal declaration

        # nonlocal false_var      # SyntaxError: no binding for nonlocal 'false_var' found

        nonlocal var02, var01, var03

        var03 = 'do_nonlocal()中nonlocal语句声明var02, var01, var03后,只赋值var03'
        print(var01, var02, var03)
        print('请留意:var01 var02 没有新赋值,变量值就不会变;nonlocal没有报错')

        print('3', globals(), '请观察实际全局变量的var')
        print('3', locals(), '请观察实际局部变量的 var和var2')

    def do_global():
        # global 先声明 后使用!
        # var = 1     # 若是不注释此行,下一行的 global var 会报错:SyntaxError: name 'var' is assigned to before global declaration

        global var
        global new_var, new_var2

        var = "do_global()新赋值的var"

        new_var = '新建全局new_var'
        # 实际 new_var2 没有赋值
        print('do_global()中global语句声明var、new_var、new_var2,没有赋值给new_var2')
        # print(new_var, var, new_var2)           # NameError: name 'new_var2' is not defined

        print(new_var, var)
        # print('请留意:var没有新赋值时,变量值就不会变;global没有报错。')
        # print('请留意:var新赋值后,变量值就变;')

        new_var2222222222 = '用完就销毁-新建'
        print('4', globals(), '留意new_var、var')
        print('4', locals())

    var = "test-var"
    print('scope_test函数中var:', var)

    var2 = 'test-var2'

    var01 = 'TEST-01'
    var02 = 'TEST-02'
    var03 = 'TEST-03'
    print(var2, var01, var02, var03, '留意')

    # print('5', globals())
    print('5', locals(), locals()['var'])

    do()
    print("do()执行后:", var, var2)

    do_2()
    print("do_2()执行后:", var, var2)               # 局部赋值(这是默认状态)不会改变 scope_test 对 var 的绑定。

    do_nonlocal()
    print("After nonlocal assignment:", var, var2, 'var、var2的值已经改变')

    print('8', globals())
    print('8', locals(), '实际局部var、var2、var03 已经改变')

    do_global()

    print('6', globals(), '实际全局var、new_var')
    print('6', locals())

    print("After global assignment 全局var 已改:", globals()['var'])
    print("After global assignment 全局new_var 新增:", globals().get('new_var'))

    # nonlocal 赋值会改变scope_test 对 var、var2 的绑定,
    # 而global 赋值会改变模块层级的绑定。


if __name__ == '__main__':
    pass

    print("最初var:", var)

    print('0', globals())       # globals()['new_var'] 报keyError
    print('0', locals())

    scope_test()

    print("最后var:", var)

    print('7', globals(), globals().get('new_var'))
    print('7', locals())

    # print('在模块层级上,locals() 和 globals() 是同一个字典。')

私有变量

https://docs.python.org/zh-cn/3.9/tutorial/classes.html#private-variables

"""
@blog: https://blog.csdn.net/zyooooxie
"""

def test_1():
    """
    类的封装、私有属性、私有方法
    :return:
    """

    # 在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,还可以自由地修改一个实例的 属性;
    # 一些属性不想被 外部访问、不想被外部操作,可以把属性的名称前加上两个下划线__。

    # 在python里,如果属性和方法前面 是双下划线,那么这个属性和方法就变成了私有的属性。
    # 以双下划线开头的方法,类的实例对象 无法使用,双下划线开头的属性 也同样无法访问;
    # 通过这种方法,就可以将 不希望外部访问的属性和方法 隐藏起来。

    # 类中的私有属性,以及私有方法只能在类的内部使用,不能被实例对象以及类对象在类外边直接调用。

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

    # 私有变量的本质
    # 类定义的时候,如果声明一个实例变量的时候,使用双下划线,Python解释器会将其改名,转换名称为"_类名__变量名"的名称,所以用原来的名字访问不到了。

    # 名称改写:任何形式为__spam 的标识符(至少带有两个前缀下划线,至多一个后缀下划线)的文本将被替换为_classname__spam,其中classname 为去除了前缀下划线的当前类名称。
    # 这种改写不考虑标识符的句法位置,只要它出现在类定义内部就会进行。

    # 那要如何访问 双下划线的私有属性呢?
    # 既然我们知道了私有变量的新名称,就可以直接从外部访问到,并修改它。
    # 因此 尽管是私有属性,我们依旧是可以对其进行更改,但建议大家不要去修改。

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

    # 在变量名前使用一个下划线,称为保护变量。
    # 可以看出,这个_addr属性根本就没有改变名称,和普通的属性一样,解释器不做任何特殊处理。
    # 这只是开发者共同的约定,看见这种变量,就如同私有变量,不要直接使用。

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

    # 私有方法的本质
    # 单下划线的方法只是开发者之间的约定,解释器不做任何改变。
    # 双下划线的方法,是私有方法,解释器会改名,改名策略和私有变量相同, 即"_类名__方法名" 。

    # 在Python中使用 _单下划线 或者 __ 双下划线 来标识一个成员被保护或者被私有化隐藏起来。
    # 但是,不管使用什么样的访问控制,都不能真正的阻止用户修改类的成员。 Python中没有绝对的安全的保护成员或者私有成员。
    # 因此,前导的下划线只是一种警告或者提醒,请遵守这个约定。除非真有必要,不要修改或者使用保护成员或者私有成员,更不要修改它们。

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

    # 1.在python中实现的封装操作,不是通过权限限制,而是通过改名(name mangling 改名策略)实现的,名字变了找不到而已。

    # 2.可以使用 __dict__ 可以查看属性(包括私有属性)的值,在类的内部使用私有属性,python内部会自动进行转换成 _类名__属性名。
    # 在类的外部不能给对象添加 私有属性,因为不能转换成_类名__属性名类型。

    # 3.可以通过 对象名._类名__方法名 访问到(但禁止这么干)。
    # 可以使用 _类名__私有属性名来获取值。但是一般情况下不要使用,了解即可。

    pass
    
"""
@blog: https://blog.csdn.net/zyooooxie
"""

class Animal(object):

    def __init__(self, age, name):
        # 类的私有属性,只能在类的内部进行访问,不能在类的外部直接访问到。
        self.__a_age = age
        self.__a_name = name

        self._a_addr = 'sz'
        self._a_hobby = '吃人'

    def __func_run(self):
        print('{}在{}跑步'.format(self.__a_name, self._a_addr))

    def _func_eat(self):
        print('{}岁啊 就喜欢{}'.format(self.__a_age, self._a_hobby))

    def run(self):
        self.__func_run()

    def eat(self):
        self._func_eat()

    # 在python中,一般定义函数名‘get_xx’来获取私有属性,定义‘set_xx’来修改私有属性。
    def set_age(self, age):
        if isinstance(age, int) is False:
            raise Exception('类型有误')

        self.__a_age = age

    def get_age(self):
        return self.__a_age

    def get_hobby(self):
        return self._a_hobby

"""
@blog: https://blog.csdn.net/zyooooxie
"""

if __name__ == '__main__':
    pass
	
	a = Animal(1, '刚1岁的小猫')
    print(a.__dict__)

    a.age = 100.12
    # age属性 若是暴露出来了,别人在使用时就可能会犯这样的错误,
    # 属性隐藏起来,并非不让外部使用,而是在使用时 要受到控制。

    print(a.__dict__)
    print(a.age, '刚设置的age')
    print(a.get_age(), '查看实际的_Animal__a_age')

    # a.set_age(100.12)       # Exception: 类型有误

    a.set_age(90)
    print(a.get_age(), '新设置后的_Animal__a_age')

    print(Animal.__dict__)

"""
@blog: https://blog.csdn.net/zyooooxie
"""

if __name__ == '__main__':
    pass
	
	print(Animal.__dict__)

    # ani = Animal(33, '33岁的小猫')
    # print(dir(ani))         # 返回 所有属性的列表 list of strings
    # print(ani.__dict__)     # 一个字典,键为属性名,值为属性值;an object’s (writable) attributes
    #
    # # dir()用来寻找一个对象的所有属性,包括__dict__中的属性,__dict__是dir()的子集
    #
    # print()

    an = Animal(5, '5岁的小猫')

    print(an.__dict__)          # {'_Animal__a_age': 5, '_Animal__a_name': '5岁的小猫', '_a_addr': 'sz', '_a_hobby': '吃人'}

    print(an._Animal__a_age)              # 不要直接使用
    print(an._Animal__a_name)             # 不要直接使用

    an._Animal__a_age = 100.01          # 绕过,直接设置了
    an._Animal__a_name = '新的name'

    print(an._Animal__a_age)
    print(an._Animal__a_name)

    print(an.__dict__, '直接设置后')

    setattr(an, '_Animal__a_age', 9.99)
    setattr(an, '_Animal__a_name', '新的name在在在')
    print(an.__dict__, 'setattr()重新赋值1')

    print()

    print(an._a_addr)                 # 不要直接使用
    print(an._a_hobby)                # 不要直接使用

    setattr(an, '_a_addr', '换个地方')
    setattr(an, '_a_hobby', '换个喜好')

    print(an.__dict__, 'setattr()重新赋值2')

    print(an._a_addr)
    print(an._a_hobby)

    an._a_addr = 'zai换个地方'
    an._a_hobby = 'zai换个喜好'
    print(an.__dict__, '直接赋值2')

    an._addr = '新新新添加个保护_addr'          # 新增
    print(an._addr)
    print(an.__dict__)

    an.__a_name = '新新新添加个私有__a_name'        # 新增
    print(an.__a_name)

    # 表面上看,外部代码“成功”地设置了 __a_name,但实际上这个 __a_name 和class内部的 __a_name 不是一个变量!
    # 内部的__a_name 已经被Python解释器自动改成了 _Animal__a_name,而外部代码给an新增了一个 __a_name。
    print(an.__dict__, an.__dict__['_Animal__a_name'], an.__dict__['__a_name'])

    # print(an.__age)                     # AttributeError: 'Animal' object has no attribute '__age'
    # print(an.__like)                    # AttributeError: 'Animal' object has no attribute '__like'

    print()

    # an.__func_run()                          # AttributeError: 'Animal' object has no attribute '__func_run'
    # print(an.__a_age)                           # AttributeError: 'Animal' object has no attribute '__a_age'

    # __func_run()方法是以双下划线开头的;这样的方法,类的对象无法使用,双下划线开头的属性也同样无法访问,
    # 通过这种方法,就可以将不希望外部访问的属性和方法隐藏起来。

    an._func_eat()                                      # 不要直接使用
    an._Animal__func_run()                              # 不要直接使用
    an.eat()
    an.run()

    print(an.get_hobby())
    print(an.get_age())

@property

https://docs.python.org/zh-cn/3.9/library/functions.html#property

"""
@blog: https://blog.csdn.net/zyooooxie
"""

def test_0():
    """
    属性装饰器 -- 把 get/set 方法“装饰”成属性调用。
    :return:
    """
    # 我们自定义get_age和set_age方法操作属性,的确可以实现私有变量的管理。那有没有简单的方式呢?

    # @property可以把一个实例方法 变成 其同名属性,以支持.号访问,它亦可标记设置限制,加以规范。

    # Decorators make defining new properties or modifying existing ones easy:
    #
    # class C(object):
    #     @property
    #     def x(self):
    #         "I am the 'x' property."
    #         return self._x
    #     @x.setter
    #     def x(self, value):
    #         self._x = value
    #     @x.deleter
    #     def x(self):
    #         del self._x

    # 使用property装饰器的时候这三个方法同名。
    # property装饰器必须在前,setter,deleter装饰器在后。

    # property装饰器:后面跟的函数名是以后的属性名。它就是getter。这个必须有,有了它至少是只读属性。
    # setter装饰器:与属性名同名,且接收2个参数,第一个self,第二个是将要赋值的值。有了它,属性可写。
    # deleter装饰器:可以控制是否删除属性,很少用。

    pass

私有实例属性使用property装饰器

"""
@blog: https://blog.csdn.net/zyooooxie
"""

class Person2(object):
    """
    property装饰器 可以用调用属性的形式来调用方法 [这个方法就变成了一个属性] ,后面不需要加()。
    把实例的某些属性保护起来,不让外部直接访问;外部使用getter读取属性和setter方法设置属性。
    """

    def __init__(self, name, age=18):
        self.p_name = name
        self.__p_age = age
        self.__p_birthday = '0101'

    @property               # 把一个getter方法变成属性,只需要加上@property就可以了
    def age(self):
        """Person2-这是 __doc__ """
        return self.__p_age

    # 以上 @property 装饰器会将 age() 方法转化为一个具有相同名称的只读属性的 "getter",
    # 并将 age 的文档字符串设置为 "Person2-这是 __doc__ "
    # 特征属性对象具有 getter, setter 以及 deleter 方法,它们可用作装饰器来创建该特征属性的副本,并将相应的访问函数设为所装饰的函数。

    # 用户进行属性调用的时候,直接调用 age 即可,而不用知道属性名 __p_age,因此用户无法更改属性,从而保护了类的属性。

    @age.setter             # 负责把一个setter方法变成属性赋值
    def age(self, new_age):
        if not isinstance(new_age, int):
            raise ValueError('age must be an integer!')

        if new_age < 0 or new_age > 100:
            print('不能设置成功,但不报错')
            return

        self.__p_age = new_age

    @age.deleter
    def age(self):
        del self.__p_age

    @property       # 还可以定义只读属性,只定义getter方法,不定义setter方法就是一个只读属性【这个属性可以让用户使用,但没办法随意修改】
    def birthday(self):
        return self.__p_birthday

    # 要特别注意:属性的方法名 不要和 属性 重名!

"""
@blog: https://blog.csdn.net/zyooooxie
"""

if __name__ == '__main__':
    pass
	
	print(Person2.__dict__)                 # 'age': <property object>

    p1 = Person2('zy1111', 1111)
    print(p1.age, '这是age')
    print(p1.__dict__)

    # p1.age = 10.7            # ValueError: age must be an integer!
    # print(p1.age)

    p1.age = 999                 # 不符合,故而 设置不成功
    print(p1.age, '那')
    print(p1.__dict__)

    p1.age = 28                 # 符合,设置成功
    print(p1.age, '符合,设置成功后的新值')
    print(p1.__dict__)

    p1._Person2__p_age = 111222.333          # 绕过了
    print(p1.age, '绕过去了')
    print(p1.__dict__)

    del p1.age
    print(p1.__dict__, '实际删除的是 _Person2__p_age ')

    print()
    print(p1.birthday)

    # p1.birthday = '0202'         # AttributeError: can't set attribute

    # del p1.birthday              # AttributeError: can't delete attribute

    p1._Person2__p_birthday = ['0202', '0303']       # 绕过了
    print(p1.__dict__)

    print(Person2.__dict__)         # 'age': property object,未变

"""
@blog: https://blog.csdn.net/zyooooxie
"""

if __name__ == '__main__':
    pass
	
    print(Person2.__dict__)             # 'age': <property object>
    print(Person2.age, Person2.age.__doc__)
    print()

    p2 = Person2('zy2222', 22)
    print(p2.__dict__)
    print(dir(p2))
    print(p2.age, '看清楚')
    print()

    # 千万不要随意修改类属性 (尤其是 值为 property object 的)
    Person2.age = '2sss'                # 修改类属性 'age': property object 变为 'age': '2sss'
    print(Person2.__dict__)

    p = Person2('zy', 11)
    print(p.__dict__)
    print(dir(p))

    # 实例是没有这个age属性(实例是有_Person2__p_age),用了 类属性age
    print(p.age, '看清楚')
    print(p.p_name, p.age, p._Person2__p_age)
    print(p.__dict__)

    p.age = '8888zy'            # 给了实例属性
    print(p.p_name, p.age, p._Person2__p_age)
    print(p.__dict__, p.__dict__['_Person2__p_age'], p.__dict__['age'])

    del Person2.age
    print(Person2.__dict__)

    # 和 birthday 做个对比       'birthday': <property object>
    # p.birthday = '0808'             # 实际报错 AttributeError: can't set attribute
    del p.birthday                      # AttributeError: can't delete attribute

私有类属性使用property装饰器

"""
@blog: https://blog.csdn.net/zyooooxie
"""

import random
import string
from xxx_use.import_external_coupon import ImportExternalCoupon
from user_log import Log


class ImportExternalCouponNew(object):

    __EXCEL_PATH = r'D:\project_python\excel_folder'

    def __init__(self, start_num: str, middle_num: str, end_num: str, num: int = 100, beginning_letter: str = None,
                 mixture_repeat_number: int = None):
        """
        num是Excel生成的数量;beginning_letter是 是否添加字母;mixture_repeat_number是 混合生成Excel时 重复的条数;
        :param start_num:
        :param middle_num:
        :param end_num:
        :param num:
        :param beginning_letter:
        :param mixture_repeat_number:
        """

        Log.info('每次生成 {} 条'.format(num))
        Log.info('start_num = {}'.format(start_num))
        Log.info('middle_num = {}'.format(middle_num))
        Log.info('end_num = {}'.format(end_num))

        self.__start_num = start_num
        self.__middle_num = middle_num
        self.__end_num = end_num
        self.__beginning_letter = beginning_letter
        self.__mixture_repeat_number = mixture_repeat_number
        self.__num = num


    @property
    def EXCEL_PATH(self):
        return self.__EXCEL_PATH

    @EXCEL_PATH.setter
    def EXCEL_PATH(self, new_path):
        if isinstance(new_path, int):
            raise Exception('搞错了。。。{}'.format(new_path))
        
        self.__EXCEL_PATH = new_path

    @property
    def num(self):
        return self.__num

    @num.setter
    def num(self, new_num):
        if isinstance(new_num, int) and new_num > 0:

            self.__num = new_num
        else:
            Log.info('传参有误,{}'.format(new_num))
            raise
            
"""
@blog: https://blog.csdn.net/zyooooxie
"""

if __name__ == '__main__':

    print(ImportExternalCouponNew.__dict__)

    iecn = ImportExternalCouponNew('123', '456', '789', 200)
    print(iecn.__dict__)
    print(dir(iecn))
    print(iecn.EXCEL_PATH)

    # iecn.EXCEL_PATH = 123456            # Exception: 搞错了。。。123456

    print()

    iecn.EXCEL_PATH = r'D:\project_python\zy'
    print(iecn.EXCEL_PATH, '新赋值')

    # 上面对iecn.EXCEL_PATH 重新赋值后,
    # 特别看下:实例对象的 _ImportExternalCouponNew__EXCEL_PATH 的值 和 类对象的 _ImportExternalCouponNew__EXCEL_PATH 的值 是不同的!

    print(iecn.__dict__)                            # '_ImportExternalCouponNew__EXCEL_PATH': 'D:\\project_python\\zy'
    print(ImportExternalCouponNew.__dict__)         # '_ImportExternalCouponNew__EXCEL_PATH': 'D:\\project_python\\excel_folder'

    # 'EXCEL_PATH': property object  未改变

一个错误的示例 如下:


"""
@blog: https://blog.csdn.net/zyooooxie
"""

if __name__ == '__main__':
    pass

    # ImportExternalCouponNew._ImportExternalCouponNew__EXCEL_PATH = r'D:\project_python\202112'        # 修改成功;未修改 EXCEL_PATH:property object
    # print(ImportExternalCouponNew.__dict__)

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

    print(ImportExternalCouponNew.__dict__)
    # 'EXCEL_PATH': <property object > ,使用property
    # 'num': <property object >

    ImportExternalCouponNew.EXCEL_PATH = r'D:\project_python\202201'          # 修改了原本的 property object;但是 _ImportExternalCouponNew__EXCEL_PATH的值是 没变
    print(ImportExternalCouponNew.__dict__)

    print()

    en = mn = sn = random.randint(100000, 999999)
    iecn = ImportExternalCouponNew(str(sn), str(mn), str(en), num=100)

    print(iecn.__dict__)
    print(iecn.EXCEL_PATH)

    iecn.EXCEL_PATH = r'D:\project_python\zy'         # 实例对象.EXCEL_PATH是 覆盖 类对象的.EXCEL_PATH
    # iecn.num = 500.22                                 # 传参有误,500.22 实际报错:RuntimeError: No active exception to reraise

    print(iecn.EXCEL_PATH, iecn.num, '刚刚新赋值')

    iecn.EXCEL_PATH = 123456                        # 没有报错!!!
    print(iecn.EXCEL_PATH)

    print()

    print(iecn.__dict__)                                # 留意 EXCEL_PATH
    print(ImportExternalCouponNew.__dict__)             # 留意 EXCEL_PATH
    

本文链接:https://blog.csdn.net/zyooooxie/article/details/108095932

交流技术 欢迎+QQ 153132336 zy
个人博客 https://blog.csdn.net/zyooooxie

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值