Python高级 -- 06 Python提高

一、私有化


xx: 公有变量


_x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问


__xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到)


__xx__:双前后下划线,用户名字空间的魔法对象或属性。例如:__init__ , __ 不要自己发明这样的名字


xx_:单后置下划线,用于避免与Python关键词的冲突


总结


父类中属性名为__名字的,子类不继承,子类不能访问


如果在子类中向__名字赋值,那么会在子类中定义的一个与父类相同名字的属性


_名的变量、函数、类在使用from xxx import *时都不会被导入


二、多继承及MRO顺序



1、多继承中,单独通过父类名称调用父类方法举例:


# coding=utf-8

print("******多继承使用类名.__init__ 发生的状态******")


class Parent(object):
    def __init__(self, name):
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')


class Son1(Parent):
    def __init__(self, name, age):
        print('Son1的init开始被调用')
        self.age = age
        Parent.__init__(self, name)
        print('Son1的init结束被调用')


class Son2(Parent):
    def __init__(self, name, gender):
        print('Son2的init开始被调用')
        self.gender = gender
        Parent.__init__(self, name)
        print('Son2的init结束被调用')


class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        Son1.__init__(self, name, age)  # 单独调用父类的初始化方法
        Son2.__init__(self, name, gender)
        print('Grandson的init结束被调用')

gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)


print("******多继承使用类名.__init__ 发生的状态******\n\n")


运行结果:


******多继承使用类名.__init__ 发生的状态******
Grandson的init开始被调用
Son1的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son1的init结束被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用类名.__init__ 发生的状态******


分析:


        使用父类名称调用父类init方法的时候,由于是多继承,孙子类继承的没个父类中都调用了顶级父类的init方法,每次调用父类的init方法的时候,都会调用顶级父类的init方法,导致顶级父类的init方法被调用多次。



2、多继承中使用super调用所有父类中被重写的方法


print("******多继承使用super().__init__ 发生的状态******")


class Parent(object):
    def __init__(self, name, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')


class Son1(Parent):
    def __init__(self, name, age, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son1的init开始被调用')
        self.age = age
        super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son1的init结束被调用')


class Son2(Parent):
    def __init__(self, name, gender, *args, **kwargs):  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son2的init开始被调用')
        self.gender = gender
        super().__init__(name, *args, **kwargs)  # 为避免多继承报错,使用不定长参数,接受参数
        print('Son2的init结束被调用')


class Grandson(Son1, Son2):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        # 多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍
        # 而super只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
        # super(Grandson, self).__init__(name, age, gender)
        super().__init__(name, age, gender)
        print('Grandson的init结束被调用')


print(Grandson.__mro__)
# mro就是子类调用父类的时候的调用顺序,子类是Grandson时候,首先调用Son1,然后调用Son2,在调用Parent
# (<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)


gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)
print('性别:', gs.gender)
print("******多继承使用super().__init__ 发生的状态******\n\n")

运行结果:


******多继承使用super().__init__ 发生的状态******
(<class '__main__.Grandson'>, <class '__main__.Son1'>, <class '__main__.Son2'>, <class '__main__.Parent'>, <class 'object'>)
Grandson的init开始被调用
Son1的init开始被调用
Son2的init开始被调用
parent的init开始被调用
parent的init结束被调用
Son2的init结束被调用
Son1的init结束被调用
Grandson的init结束被调用
姓名: grandson
年龄: 12
性别: 男
******多继承使用super().__init__ 发生的状态******



3、单继承中使用super调用父类方法


print("******单继承使用super().__init__ 发生的状态******")


class Parent(object):
    def __init__(self, name):
        print('parent的init开始被调用')
        self.name = name
        print('parent的init结束被调用')


class Son1(Parent):
    def __init__(self, name, age):
        print('Son1的init开始被调用')
        self.age = age
        super().__init__(name)  # 单继承不能提供全部参数
        print('Son1的init结束被调用')


class Grandson(Son1):
    def __init__(self, name, age, gender):
        print('Grandson的init开始被调用')
        super().__init__(name, age)  # 单继承不能提供全部参数
        print('Grandson的init结束被调用')

gs = Grandson('grandson', 12, '男')
print('姓名:', gs.name)
print('年龄:', gs.age)


print("******单继承使用super().__init__ 发生的状态******\n\n")

"""
    运行结果:
        ******单继承使用super().__init__ 发生的状态******
        Grandson的init开始被调用
        Son1的init开始被调用
        parent的init开始被调用
        parent的init结束被调用
        Son1的init结束被调用
        Grandson的init结束被调用
        姓名: grandson
        年龄: 12
        ******单继承使用super().__init__ 发生的状态******
""" 


4、以上三种方式总结


super().__init__相对于类名.__init__,在单继承上用法基本无差。


但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,具体看前面的输出结果。


多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错。


单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错。


多继承时,相对于使用类名.__init__方法,要把每个父类全部写一遍, 而使用super方法,只需写一句话便执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因。


三、静态方法、类方法



1、类属性、实例属性


class Province(object):
    # 类属性
    country = '中国'

    def __init__(self, name):
        # 实例属性
        self.name = name


# 创建一个实例对象
obj = Province('山东省')
# 直接访问实例属性
print(obj.name)
# 直接访问类属性
Province.country


实例属性属于对象,类属性属于类


类属性在内存中只保存一份,实例属性在每个对象中都要保存一份


2、实例方法、静态方法和类方法


方法包括:实例方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。


        实例方法:由对象调用;至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self;


        类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;


        静态方法:由类调用;无默认参数;


class Foo(object):
    def __init__(self, name):
        self.name = name

    def ord_func(self):
        """ 定义实例方法,至少有一个self参数 """
        # print(self.name)
        print('实例方法')

    @classmethod
    def class_func(cls):
        """ 定义类方法,至少有一个cls参数 """
        print('类方法')

    @staticmethod
    def static_func():
        """ 定义静态方法 ,无默认参数"""
        print('静态方法')


f = Foo("中国")
# 调用实例方法
f.ord_func()

# 调用类方法
Foo.class_func()

# 调用静态方法
Foo.static_func()


四、property属性⭐⭐⭐



1、什么是property属性


一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法


# ############### 定义 ###############
class Foo:
    def func(self):
        pass

    # 定义property属性
    @property
    def prop(self):
        pass

# ############### 调用 ###############
foo_obj = Foo()
foo_obj.func()  # 调用实例方法
foo_obj.prop  # 调用property属性


property属性调用的时候注意以下几点:


        定义时,在实例方法的基础上添加@property装饰器,并且有且只有一个参数self


        调用时无需括号即可调用


2、property属性的Demo:分页


# ############### 定义 ###############
class Pager:
    def __init__(self, current_page):
        # 用户当前请求的页码(第一页、第二页...)
        self.current_page = current_page
        # 每页默认显示10条数据
        self.per_items = 10 

    @property
    def start(self):
        val = (self.current_page - 1) * self.per_items
        return val

    @property
    def end(self):
        val = self.current_page * self.per_items
        return val

# ############### 调用 ###############
p = Pager(1)
p.start  # 就是起始值,即:m
p.end  # 就是结束值,即:n


理解:Python中的property属性的功能是:property内部进行一系列的逻辑计算,最终将计算的结果返回。


3、property属性创建的方式



(1)、装饰器:即在类的实例方法上面加上@property装饰器


Python中的类有经典类新式类新式类的属性比经典类的属性丰富。( 如果类继object,那么该类是新式类 )


经典类中


# ############### 定义 ###############    
class Goods:
    @property
    def price(self):
        return "laowang"
    
# ############### 调用 ###############
obj = Goods()
result = obj.price  # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
print(result)


新式类中:具有三种@property装饰器


#coding=utf-8
# ############### 定义 ###############
class Goods:
    """python3中默认继承object类
        以python2、3执行此程序的结果不同,因为只有在python3中才有@xxx.setter  @xxx.deleter
    """
    @property
    def price(self):
        print('@property')

    @price.setter
    def price(self, value):
        print('@price.setter')

    @price.deleter
    def price(self):
        print('@price.deleter')

# ############### 调用 ###############
obj = Goods()
obj.price          # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
obj.price = 123    # 自动执行 @price.setter 修饰的 price 方法,并将  123 赋值给方法的参数
del obj.price      # 自动执行 @price.deleter 修饰的 price 方法



注意


        经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法


        新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法


由于新式类中具有三种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除


class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
del obj.price     # 删除商品原价


(2)、类属性:即在类中定义值为property对象的类属性


        当使用类属性的方式创建property属性时,经典类新式类无区别


class Foo:
    def get_bar(self):
        return 'laowang'

    BAR = property(get_bar)

obj = Foo()
reuslt = obj.BAR  # 自动调用get_bar方法,并获取方法的返回值
print(reuslt)


参数详解:property方法中有个四个参数


        第一个参数是方法名,调用 对象.属性 时自动触发执行方法


        第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法


        第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法


        第四个参数是字符串,调用 对象.属性.__doc__ ,此参数是该属性的描述信息


#coding=utf-8
class Foo(object):
    def get_bar(self):
        print("getter...")
        return 'laowang'

    def set_bar(self, value): 
        """必须两个参数"""
        print("setter...")
        return 'set value' + value

    def del_bar(self):
        print("deleter...")
        return 'laowang'

    BAR = property(get_bar, set_bar, del_bar, "description...")

obj = Foo()

obj.BAR  # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex"  # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
desc = Foo.BAR.__doc__  # 自动获取第四个参数中设置的值:description...
print(desc)
del obj.BAR  # 自动调用第三个参数中定义的方法:del_bar方法


由于类属性方式创建property属性具有3种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除


class Goods(object):

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    def get_price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    def set_price(self, value):
        self.original_price = value

    def del_price(self):
        del self.original_price

    PRICE = property(get_price, set_price, del_price, '价格属性描述...')

obj = Goods()
obj.PRICE         # 获取商品价格
obj.PRICE = 200   # 修改商品原价
del obj.PRICE     # 删除商品原价


(3)、总结:


        定义property属性共有两种方式,分别是【装饰器】和【类属性】,而【装饰器】方式针对经典类和新式类又有所不同。


        通过使用property属性,能够简化调用者在获取数据的流程。


4、property属性的应用



(1)、原始的私有属性的getter/setter方法


class Money(object):
    def __init__(self):
        self.__money = 0

    def getMoney(self):
        return self.__money

    def setMoney(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")


(2)、使用property升级的私有属性getter/setter方法


class Money(object):
    def __init__(self):
        self.__money = 0

    def getMoney(self):
        return self.__money

    def setMoney(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")

    # 定义一个属性,当对这个money设置值时调用setMoney,当获取值时调用getMoney
    money = property(getMoney, setMoney)  

a = Money()
a.money = 100  # 调用setMoney方法
print(a.money)  # 调用getMoney方法
#100



(3)、使用property取代getter/settter方法


class Money(object):
    def __init__(self):
        self.__money = 0

    # 使用装饰器对money进行装饰,那么会自动添加一个叫money的属性,当调用获取money的值时,调用装饰的方法
    @property
    def money(self):
        return self.__money

    # 使用装饰器对money进行装饰,当对money设置值时,调用装饰的方法
    @money.setter
    def money(self, value):
        if isinstance(value, int):
            self.__money = value
        else:
            print("error:不是整型数字")

a = Money()
a.money = 100
print(a.money)



五、魔法属性、魔法方法



1、什么是魔法属性


        python中存在着一些具有特殊含义的属性,就叫做魔法属性


2、魔法属性:__doc__

        

        表示类的描述信息


class Demo:
    """ 描述类的信息 """
    def func(self):
        pass

# 调用魔法属性
print(Demo.__doc__)     # 描述类的信息


3、魔法属性:__module__和__class__


        __module__:表示当前操作的对象在哪个模块


        __class__:表示当前操作的对象的类是什么


test.py


class Person(object):
    def __init__(self):
        self.name = "老王"


main.py


from test import Person

obj = Person()

# __module__属性返回值是当前对象所在的模块名称
print(obj.__module__)       # test

# __class__属性返回值是当前对象的类型
print(obj.__class__)        # <class 'test.Person'>


4、魔法属性:__dict__


        __dict__用来获取类或者对象中的所有属性。类的实例属性属于对象,类中的类属性和方法等属于类


class Province(object):
    country = 'China'

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

    def func(self, *args, **kwargs):
        print('func')

# 获取类的属性,即:类属性、方法、
print(Province.__dict__)
"""
    {'__module__': '__main__', 'country': 'China', '__init__': <function Province.__init__ at 0x0000020F42AFB9D8>, 
    'func': <function Province.func at 0x0000020F42AFBA60>, '__dict__': <attribute '__dict__' of 'Province' objects>, 
    '__weakref__': <attribute '__weakref__' of 'Province' objects>, '__doc__': None}

"""

# 获取 对象obj1 的属性
obj1 = Province('山东', 10000)
print(obj1.__dict__)

# 输出:{'count': 10000, 'name': '山东'}


# 获取 对象obj2 的属性
obj2 = Province('山西', 20000)
print(obj2.__dict__)

# 输出:{'count': 20000, 'name': '山西'}


5、魔法方法:__init__


        初始化方法,通过类创建对象的时候,自动触发执行该方法


class Person(object):

    def __init__(self):
        print("通过类创建对象的时候,执行了该方法")


p = Person()        # 通过类创建对象的时候,执行了该方法


6、魔法方法:__del__


        当对象在内存中被释放的时候,自动触发执行该方法。此方法一般无须定义,__del__的调用是由解释器在进行垃圾回收时自动触发执行的。


class Person(object):
   
    def __del__(self):
        print("解释器进行垃圾回收此对象的时候,执行了该方法")


7、魔法方法:__call__


        对象后面加()号,触发执行该方法。__init__方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()


class Demo:
    def __init__(self):
        print("执行 __init__")

    def __call__(self, *args, **kwargs):
        print('执行 __call__')


obj = Demo()  # 执行 __init__
obj()  # 执行 __call__



8、魔法方法:__str__


        如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。


class Foo:
    def __str__(self):
        return 'laowang'


obj = Foo()
print(obj)
# 输出:laowang


9、魔法方法:__getitem__、__setitem__、__delitem__


        用于索引操作,如字典。


        __getitem__:表示获取数据


        __setitem__:表示设置数据


        __delitem__:表示删除数据


class Foo(object):

    def __getitem__(self, key):
        print('__getitem__', key)

    def __setitem__(self, key, value):
        print('__setitem__', key, value)

    def __delitem__(self, key):
        print('__delitem__', key)


obj = Foo()

# 自动触发执行 __getitem__
result = obj['k1']

# 自动触发执行 __setitem__
obj['k2'] = 'laowang'

# 自动触发执行 __delitem__
del obj['k1']


10、魔法方法:__getslice__、__setslice__、__delslice__


        以上三个方法用于分片操作


class Foo(object):

    def __getslice__(self, i, j):
        print('__getslice__', i, j)

    def __setslice__(self, i, j, sequence):
        print('__setslice__', i, j)

    def __delslice__(self, i, j):
        print('__delslice__', i, j)

obj = Foo()

obj[1:1]                   # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44]    # 自动触发执行 __setslice__
del obj[0:2]                # 自动触发执行 __delslice__



六、with与上下文管理器⭐⭐⭐


1、with


(1)、操作文件的普通版


def m1():
    f = open("output.txt", "w")
    f.write("python之禅")
    f.close()

         这样写有一个潜在的问题,如果在调用 write 的过程中,出现了异常进而导致后续代码无法继续执行,close 方法无法被正常调用,因此资源就会一直被该程序占用者释放。那么该如何改进代码呢?


(2)、改进版


def m2():
    f = open("output.txt", "w")
    try:
        f.write("python之禅")
    except IOError:
        print("oops error")
    finally:
        f.close()


        改良版本的程序是对可能发生异常的代码处进行 try 捕获,使用 try/finally 语句,该语句表示如果在 try 代码块中程序出现了异常,后续代码就不再执行,而直接跳转到 except 代码块。而无论如何,finally 块的代码最终都会被执行。因此,只要把 close 放在 finally 代码中,文件就一定会关闭。


(3)、高级版


def m3():
    with open("output.txt", "r") as f:
        f.write("Python之禅")


使用with关键字,open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。那么它的实现原理是什么?在讲 with 的原理前要涉及到另外一个概念,就是上下文管理器(Context Manager)。


2、Context


(1)、什么是上下文


        上下文在不同的地方表示不同的含义,要感性理解。context其实说白了,和文章的上下文是一个意思,在通俗一点,我觉得叫环境更好。


(2)、上下文管理器


        任何实现了__enter__()和__exit__()方法的对象都可以称之为上下文管理器。上下文管理器对象可以使用with关键字。显然,文件(file)对象实现了上下文管理器。


(3)、自定义上下文管理器类


class MyFile():

    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        print("entering")
        self.f = open(self.filename, self.mode)
        return self.f

    def __exit__(self, *args):
        print("will exit")
        self.f.close()


__enter__()方法返回资源对象,__exit()__方法进行释放资源的操作。应用自定义的上下文管理器就可以使用一下方式。


with MyFile("out.txt","w") as f:
    print("writing")
    f.write("hello  world")


(4)、实现上下文的另一种方式


        Python 还提供了一个 contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 之前的语句在 __enter__ 方法中执行,yield 之后的语句在 __exit__ 方法中执行。紧跟在 yield 后面的值是函数的返回值。


from contextlib import contextmanager


@contextmanager
def my_open(path, mode):
    f = open(path, mode)
    yield f
    f.close()


# ---------以下是调用------------
with my_open('out.txt', 'w') as f:
    f.write("hello , the simplest context manager")


总结:Python 提供了 with 语法用于简化资源操作的后续清除操作,是 try/finally 的替代方法,实现原理建立在上下文管理器之上。此外,Python 还提供了一个 contextmanager 装饰器,更进一步简化上下管理器的实现方式。

猜你喜欢

转载自blog.csdn.net/wingzhezhe/article/details/79272472