一、概述

前面讲了python面向目标编程(OOP:Object Oriented Programming),接下来讲一下OOP剩余的一些知识点和面向切面编程 AOP,非常重要的编程思维。

Python 高级编程之面向切面编程 AOP(二)
关python环境、根底介绍、面向目标编程介绍能够参考我以下几篇文章:

  • Python 介绍和环境准备
  • Python 根底语法介绍(一)
  • Python 根底语法介绍(二)
  • Python 高档编程之面向目标(一)

二、函数装修器

1)无参函数装修器

  • python中的装修器(decorator)一般采用语法糖的办法,是一种语法格局。比如:@classmethod@staticmethod@property@xxx.setter@wraps()@func_name等都是python中的装修器。
  • 装修器,装修的目标是函数或许办法。各种装修器的作用都是相同的:改动被装修函数或许办法的功用,性质。

假设用 funA() 函数装修器去装修 funB() 函数,如下所示:

#funA 作为装修器函数
def funA(fn):
    #...
    fn() # 履行传入的fn参数
    #...
    return '...'
@funA
def funB():
    #...

实际上,上面程序彻底等价于下面的程序:

def funA(fn):
    #...
    fn() # 履行传入的fn参数
    #...
    return '...'
def funB():
    #...
funB = funA(funB)

经过比对以上 2 段程序不难发现,运用函数装修器 A() 去装修另一个函数 B(),其底层履行了如下 2 步操作:

  • 将 B 作为参数传给 A() 函数;
  • 将 A() 函数履行完成的回来值反应回 B。

示例如下:

#funA 作为装修器函数
def funA(fn):
    print("python 学习")
    fn() # 履行传入的fn参数
    print("http://python.study.net")
    return "装修器函数的回来值"
@funA
def funB():
    print("学习 Python")
if __name__ == "__main__":
    print(funB)

输出成果:

python 学习
学习 Python
http://python.study.net
装修器函数的回来值

2)带数函数装修器

在剖析 funA() 函数装修器和 funB() 函数的联系时,细心的读者可能会发现一个问题,即当 funB() 函数无参数时,能够直接将 funB 作为 funA() 的参数传入。可是,假如被润饰的函数自身带有参数,那应该怎么传值呢?

比较简单的处理办法便是在函数装修器中嵌套一个函数,该函数带有的参数个数和被装修器润饰的函数相同。例如:

def funA(fn):
    # 界说一个嵌套函数
    def say(arc):
        print("Python 学习:", arc)
        funB(arc)
    return say
@funA
def funB(arc):
    print("funB():", arc)
if __name__ == "__main__":
	funB("http://python.study.net")

输出成果:

Python 学习: http://python.study.net

这儿有必要给读者剖析一下这个程序,其实,它和如下程序是等价的:

def funA(fn):
    # 界说一个嵌套函数
    def say(arc):
        print("Python 学习:",arc)
    return say
def funB(arc):
    print("funB():", arc)
if __name__ == "__main__": 
	funB = funA(funB)
	funB("http://python.study.net")

3)嵌套函数装修器

上面示例中,都是运用一个装修器的情况,但实际上,Python 也支持多个装修器,比如:

@funA
@funB
@funC
def fun():
    #...

上面程序的履行顺序是里到外,所以它等效于下面这行代码:

fun = funA( funB ( funC (fun) ) )

三、类办法润饰

1)实例办法

通常情况下,在类中界说的办法默认都是实例办法。

class Test():
    #类构造办法,也属于实例办法
    def __init__(self):
        self.name = "Test"
        self.address = "http://www.study.net"
    # 下面界说了一个say实例办法
    def say(self):
        print("正在调用 say() 实例办法")
if __name__ == "__main__":
    t = Test()
    # 经过实例目标办法实例办法
    t.say()
    # 经过类名拜访实例目标,可是有必要传实例目标名
    Test.say(c)

2)类办法(@classmethod润饰)

Python 类办法和实例办法相似,它最少也要包括一个参数,只不过类办法中通常将其命名为 cls,Python 会主动将类自身绑定给 cls 参数(留意,绑定的不是类目标)。也便是说,咱们在调用类办法时,无需显式为 cls 参数传参。

【温馨提示】和 self 相同,cls 参数的命名也不是规定的(能够随意命名),只是 Python 程序员约定俗称的习气罢了。

和实例办法最大的不同在于,类办法需求运用@classmethod润饰符进行润饰,例如:

class Test:
    #类构造办法,也属于实例办法
    def __init__(self):
        self.name = "Test"
        self.add = "http://www.Test.net"
    #下面界说了一个类办法
    @classmethod
    def info(cls):
        print("正在调用类办法",cls)
if __name__ == "__main__":
    t = Test()
    # 经过目标调用类办法
    t.info()
    # 经过类名调用类办法,类办法引荐直接经过类名调用
    Test.info()

留意,假如没有 @classmethod,则 Python 解说器会将 info() 办法认定为实例办法,而不是类办法。

3)静态办法(@staticmethod润饰)

静态办法,其实便是咱们学过的函数,和函数仅有的区别是,静态办法界说在类这个空间(类命名空间)中,而函数则界说在程序所在的空间(大局命名空间)中。

静态办法没有相似 self、cls 这样的特殊参数,因而 Python 解说器不会对它包括的参数做任何类或目标的绑定。也正因为如此,类的静态办法中无法调用任何类特点和类办法。

静态办法需求运用@staticmethod润饰,例如:

class Test:
    @staticmethod
    def info(name, add):
        print(name, add)
if __name__ == "__main__":
    c = Test
    # 经过目标调用类办法
    c.info("测验", 123)
    # 经过类名调用类办法(引荐)
    Test.info("hello", "world")

四、 property() 函数

传统操作类特点的办法比较麻烦,更习气运用“类目标.特点”这种办法 , Python 中提供了 property() 函数,能够实现在不损坏类封装原则的前提下,让开发者仍旧运用“类目标.特点”的办法操作类中的特点。

property() 函数的根本运用格局如下:

特点名=property(fget=None, fset=None, fdel=None, doc=None)
  • fget 参数用于指定获取该特点值的类办法,
  • fset 参数用于指定设置该特点值的办法,
  • fdel 参数用于指定删去该特点值的办法,
  • doc 是一个文档字符串,用于阐明此函数的作用。

【留意】在运用 property() 函数时,以上 4 个参数能够仅指定第 1 个、或许前 2 个、或许前 3 个,当时也能够悉数指定。也便是说,property() 函数中参数的指定并不是彻底随意的。

例如,修正上面的程序,为 name 特点装备 property() 函数:

class CLanguage:
    #构造函数
    def __init__(self,n):
        self.__name = n
    #设置 name 特点值的函数
    def setname(self,n):
        self.__name = n
    #拜访nema特点值的函数
    def getname(self):
        return self.__name
    #删去name特点值的函数
    def delname(self):
        self.__name="xxx"
    #为name 特点装备 property() 函数
    name = property(getname, setname, delname, '指明出处')
#调取阐明文档的 2 种办法
#print(CLanguage.name.__doc__)
help(CLanguage.name)
clang = CLanguage("C言语中文网")
#调用 getname() 办法
print(clang.name)
#调用 setname() 办法
clang.name="Python教程"
print(clang.name)
#调用 delname() 办法
del clang.name
print(clang.name)

五、hasattr()、getattr()、setattr()函数

1)hasattr()

hasattr() 函数用来判别某个类实例目标是否包括指定名称的特点或办法。该函数的语法格局如下:

hasattr(obj, name)
  • obj 指的是某个类的实例目标,
  • name 表明指定的特点名或办法名。
  • 一起,该函数会将判别的成果(True 或许 False)作为回来值反应回来。

示例如下:

class Test001:
    def __init__ (self):
        self.name = "Test001"
        self.add = "http://www.test001.com"
    def say(self):
        print("我正在学Python")
if __name__ == "__main__":
    obj = Test001()
    print(hasattr(obj, "name"))
    print(hasattr(obj, "add"))
    print(hasattr(obj, "say"))
    print(hasattr(obj, "fly"))

输出结构:

True
True
True
False

2)getattr()

getattr() 函数获取某个类实例目标中指定特点的值。没错,和 hasattr() 函数不同,该函数只会从类目标包括的一切特点中进行查找

getattr() 函数的语法格局如下:

getattr(obj, name[, default])
  • obj 表明指定的类实例目标,
  • name 表明指定的特点名,
  • default 是可选参数,用于设定该函数的默认回来值,即当函数查找失利时,假如不指定 default 参数,则程序将直接报 AttributeError 过错,反之该函数将回来 default 指定的值。

示例如下:

class Test002:
    def __init__ (self):
        self.name = "Test002"
        self.add = "http://www.test002.com"
    def say(self):
        print("我正在学Python")
if __name__ == "__main__":
    obj = Test002()
    print(getattr(obj, "name"))
    print(getattr(obj, "add"))
    print(getattr(obj, "say"))
    print(getattr(obj, "display", 'nodisplay'))

输出成果:

Test002
http://www.test002.com
<bound method Test002.say of <__main__.Test002 object at 0x00000193F9B98A20>>
nodisplay

3)setattr()

setattr() 函数的功用相对比较复杂,它最根底的功用是修正类实例目标中的特点值。其次,它还能够实现为实例目标动态增加特点或许办法。

setattr() 函数的语法格局如下:

setattr(obj, name, value)

示例如下:

class Test003:
    def __init__ (self):
        self.name = "Test003"
        self.add = "http://www.test003.com"
    def say(self):
        print("我正在学Python")
if __name__ == "__main__":
    obj = Test003()
    print(obj.name)
    print(obj.add)
    setattr(obj, "name", "Python教程")
    setattr(obj, "add", "http://www.test003.net")
    print(obj.name)
    print(obj.add)

输出成果:

Test003
http://www.test003.com
Python教程
http://www.test003.net

六、issubclass()和isinstance()函数

Python 提供了如下两个函数来检查类型:

  • issubclass(cls, class_or_tuple):检查 cls 是否为后一个类或元组包括的多个类中恣意类的子类。
  • isinstance(obj, class_or_tuple):检查 obj 是否为后一个类或元组包括的多个类中恣意类的目标。

示例如下:

# 界说一个字符串
hello = "Hello";
# "Hello"是str类的实例,输出True
print('"Hello"是否是str类的实例: ', isinstance(hello, str))
# "Hello"是object类的子类的实例,输出True
print('"Hello"是否是object类的实例: ', isinstance(hello, object))
# str是object类的子类,输出True
print('str是否是object类的子类: ', issubclass(str, object))
# "Hello"不是tuple类及其子类的实例,输出False
print('"Hello"是否是tuple类的实例: ', isinstance(hello, tuple))
# str不是tuple类的子类,输出False
print('str是否是tuple类的子类: ', issubclass(str, tuple))
# 界说一个列表
my_list = [2, 4]
# [2, 4]是list类的实例,输出True
print('[2, 4]是否是list类的实例: ', isinstance(my_list, list))
# [2, 4]是object类的子类的实例,输出True
print('[2, 4]是否是object类及其子类的实例: ', isinstance(my_list, object))
# list是object类的子类,输出True
print('list是否是object类的子类: ', issubclass(list, object))
# [2, 4]不是tuple类及其子类的实例,输出False
print('[2, 4]是否是tuple类及其子类的实例: ', isinstance([2, 4], tuple))
# list不是tuple类的子类,输出False
print('list是否是tuple类的子类: ', issubclass(list, tuple))

七、面向切面编程( AOP )

AOP(Aspect Orentied Programming) 简言之、这种在运行时,编译时,类和办法加载时,动态地将代码切入到类的指定办法、指定位置上的编程思维便是面向切面的编程。这样对原有代码毫无入侵性。

先来了解一下什么是装修器吧?

其实上面也经过示例略微解说了python自带的几个常用的装修器函数(@classmethod@staticmethod),其实装修器是一个很著名的规划形式之一(后面会要点解说23种规划形式),装修器经常被用于有切面需求的场景,较为经典的有刺进日志、功用测验、事务处理等。装修器是处理这类问题的绝佳规划,有了装修器,咱们就能够抽离出大量函数中与函数功用自身无关的相同代码并继续重用。简而言之,装修器的作用便是为现已存在的目标增加额外的功用。

其实上面现已有示例了,那咱们再来看几个示例加深一下理解:

import time
def timeit(func):
    def wrapper():
        start = time.clock()
        func()
        end =time.clock()
        print 'used:', end - start
    return wrapper
@timeit
def foo():
    print 'in foo()'
foo()

在界说上加上@timeit这一行与另外写foo = timeit(foo)彻底等价。

import time
def timeit(func):
    def wrapper():
        start = time.clock()
        func()
        end =time.clock()
        print 'used:', end - start
    return wrapper
def foo():
    print 'in foo()'
foo = timeit(foo)

内置的装修器有三个,分别是staticmethodclassmethodproperty,作用分别是把类中界说的实例办法变成静态办法、类办法和类特点。其实上面有详细的示例解说,这儿就不再细心解说了。再看个示例加深一下理解。

class Rabbit(object):
    def __init__(self, name):
        # 只读特点"_",私有特点"__"
        self._name = name
    @staticmethod
    def newRabbit(name):
        return Rabbit(name)
    @classmethod
    def newRabbit2(cls):
        return Rabbit('')
    @property
    def name(self):
        return self._name
    # 这儿界说的特点是一个只读特点,假如需求再类外可写,则需求再界说一个setter:
    @name.setter
    def name(self, name):
        self._name = name
if __name__ == "__main__":
    r = Rabbit("test")
    print(r.name)
    r.name = "hello"
    print(r.name)

Python 高档编程之面向切面编程(AOP)就先到这儿,后续会持续更相关文档,有任何疑问欢迎给我留言,也能够关注我的公众号【大数据与云原生技能分享】深入技能交流~

Python 高级编程之面向切面编程 AOP(二)