目录
1.1 namespace (命名空间)
1.2 属性
1.3 命名空间的生存期
1.4 作用域
1.4.1 全局变量
1.4.2 局部变量
1.5 作用域和命名空间示例
2 类
2.1.1 类定义
2.1.2 类方法(可以先看后面)
2.2 类对象
2.2.1 属性引用
2.2.2 实例化
2.3 实例对象
2.3.1 数据属性
2.3.2 方法
2.4 方法对象
2.4.1 调用
2.5 类变量和实例变量
2.5.1 类变量共享(避免使用)
2.5.2 实例变量私有(通常用法)
3 继承
4 多重继承
4.1 父类调用
4.2 super() 功能
super() 扩展:
函数的装饰器(由方法到函数)
1.1 namespace (命名空间)
定义:是一个从名字到对象的映射。(一般不用关注)
对象的属性集合也是一种命名空间的形式。
1.2 属性
定义:任何跟在一个点号之后的名称都称为 属性 --- 例如,在表达式 z.real 中,real 是对象 z 的一个属性。
在表达式 modname.funcname 中,modname 是一个模块对象而 funcname 是它的一个属性。在此情况下在 模块的属性 和 模块 中定义的全局名称之间正好存在一个直观的映射:它们共享相同的命名空间!
1.3 命名空间的生存期
不同时刻创建的命名空间拥有不同的生存期。
- 包含内置名称的命名空间是在 Python 解释器启动时创建的,永远不会被删除。((内置名称实际上也存在于一个模块中;这个模块称作 builtins 。))
- 模块的全局命名空间在模块定义被读入时创建,也会持续到解释器退出。(被解释器的顶层调用执行的语句,从一个脚本文件读取或交互式地读取,被认为是 __main__ 模块调用的一部分,因此它们拥有自己的全局命名空间。)
- 一个函数的本地命名空间在这个函数被调用时创建,并在函数返回或抛出一个不在函数内部处理的错误时被删除。
1.4 作用域
定义:一个 作用域 是一个命名空间可直接访问的 Python 程序的文本区域。
“可直接访问” 意味着对名称的 非限定引用 会在 命名空间 中查找名称。
- 最先搜索的最内部作用域包含局部名称(函数内部)
- 从最近的封闭作用域开始搜索的任何封闭函数的范围包含非局部名称,也包括全局名称
- 倒数第二个作用域包含当前模块的全局名称
- 最外面的范围(最后搜索)是包含内置名称的命名空间
1.4.1 全局变量
如果一个名称被声明为全局变量,则所有引用和赋值将直接指向包含该模块的全局名称的中间作用域。
1.4.2 局部变量
要重新绑定在最内层作用域以外找到的变量,可以使用 nonlocal 语句声明为非本地变量。如果没有被声明为非本地变量,这些变量将是只读的(尝试写入这样的变量只会在最内层作用域中创建一个 新的 局部变量,而同名的外部变量保持不变)。( 类似于函数内部的变量)
通常,当前局部作为域将引用当前函数的局部名称。 在函数以外,局部作用域将引用与全局作用域相一致的命名空间:模块的命名空间。 类 定义将在局部命名空间内再放置另一个命名空间。(在一个模块内定义的函数的全局作用域就是该模块的命名空间,无论该函数从什么地方或以什么别名被调用。)
如果不存在生效的 global 语句 -- 对名称的赋值总是进入最内层作用域。赋值不会复制数据 --- 它们只是将名称绑定到对象(类似于指针)。
global 语句可被用来表明特定变量生存于全局作用域并且应当在其中被重新绑定;nonlocal 语句表明特定变量生存于外层作用域中并且应当在其中被重新绑定。
1.5 作用域和命名空间示例
def scope_test():
def do_local():
spam = "local spam"
def do_nonlocal():
nonlocal spam
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam"
spam = "test spam"
do_local()
print("After local assignment:", spam)
do_nonlocal()
print("After nonlocal assignment:", spam)
do_global()
print("After global assignment:", spam)
scope_test()
print("In global scope:", spam)
输出
After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
将 spam 名称(标签)(可写)挂到 "test spam" 对象(物品)(只读)上,通过输出发现只改变了最内层作用域的标签。(意思大概是函数内部发生的事只会影响函数内部的作用域以及作用域内的对象)
局部 赋值(这是默认状态)不会改变 scope_test 对 spam 的绑定。 nonlocal 赋值会改变 scope_test 对 spam 的绑定,而 global 赋值会改变模块层级的绑定。
2 类
2.1.1 类定义
- 类定义与函数定义 (def 语句) 一样必须 被执行 才会起作用。
- 在实践中,类定义内的语句通常都是 函数定义 ,但也允许有其他语句
- 类内部的函数定义通常具有一种 特别形式的参数列表,这是方法调用的约定规范所指明的
- 进行类定义时,将创建一个新的命名空间,并将其用作局部作用域-- 因此,所有对局部变量的赋值都是在这个新命名空间之内。 特别的,函数定义会绑定到这里的新函数名称。
- 离开类定义时,将创建一个 类对象(也就是类的名称)。 这基本上是一个包围在类定义所创建命名空间内容周围的包装器;
2.1.2 类方法(可以先看后面)
Python中3种方式定义类方法, 常规方式 (self), @classmethod修饰方式, @staticmethod修饰方式。
- 普通的类方法,需要通过 self参数 隐式传递 当前类的实例对象。
- @classmethod修饰的方法,需要传递当前类对象 参数cls(调用时可以不写)。
- @staticmethod修饰的方法,定义与普通函数是一样的,不需要传实例对象和类对象。
@classmethod,@staticmethod修饰方式区别:
- @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
- @classmethod也不需要self参数,但第一个参数需要表示自身类的cls参数。
- 如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
- 而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。
简单示例:
class A(object):
bar = 1
def foo(self):
print('foo')
@staticmethod
def static_foo():
print('static_foo')
print(A.bar)
@classmethod
def class_foo(cls):
print('class_foo')
print(cls.bar)
cls().foo()
A.static_foo()
A.class_foo()
# 输出结果
static_foo
1
class_foo
1
foo
2.2 类对象
类对象支持两种操作:属性引用 和 实例化 。
2.2.1 属性引用
属性引用 使用 Python 中所有属性引用所使用的标准语法: obj.name。
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
MyClass.i , MyClass.f , MyClass.__doc__ 都是有效的属性引用。
MyClass.i 返回 12345 (一个int对象)
MyClass.f 返回 <function __main__.MyClass.f(self)>(一个函数对象)
MyClass.__doc__ 返回 “A simple example class” (一个字符串对象)
2.2.2 实例化
类的 实例化 使用 函数表示法 。 可以把类对象视为是返回 该类的一个新实例的 不带参数的函数。
x = MyClass()
创建类的新 实例 并将此对象分配给局部变量 x 。实例化操作(“调用”类对象)会创建一个 空对象 。
如果需要创建一个带有初始状态的实例(对象),需要在类定义时包含一个名为 __init__() 的特殊方法。
def __init__(self):
self.data = []
__init__() 方法
当一个类定义了 __init__() 方法时,类的实例化操作会自动为新创建的类实例发起调用 __init__()。
class Complex:
def __init__(self, realpart, imagpart):
self.r = realpart
self.i = imagpart
x = Complex(3.0, -4.5)
x.r, x.i
x,r 返回 3.0
x.i 返回 -4.5
2.3 实例对象
实例对象理解的唯一操作是 属性引用 。有两种有效的属性名称,数据属性 和 方法 。
2.3.1 数据属性
x.counter = 1
while x.counter < 10:
x.counter = x.counter * 2
print(x.counter)
del x.counter
print 输出 16,通过 x.counter 创建 count 数据属性,并通过 del 删除。
2.3.2 方法
另一类实例属性引用称为 方法 。 方法是“从属于”对象的 函数 。函数一般与对象无关,像一些lambda函数、python内置函数等。
作用域:方法是通过实例化的对象进行方法的调用,调用后开辟的空间不会释放,而函数则不同,函数执行完后,为其开辟的内存空间立即释放(存储到了栈里)。(参考命名空间和作用域的生存期)
实例对象的有效方法名称依赖于其所属的类。一个类中所有是函数对象的属性都是定义了其实例的相应方法。
在示例中,x.f 是有效的方法引用,因为 MyClass.f 是一个函数,而 x.i 不是方法,因为 MyClass.i 不是一个函数。 但是 x.f 与 MyClass.f 并不是一回事 --- 它是一个 方法对象,不是函数对象。(即类在实例化后,类中的函数对象会变成实例的方法对象)
2.4 方法对象
2.4.1 调用
函数是通过“函数名()”的方式调用,方法通过“对象.方法名()”的方式进行调用。
立即调用
x.f()
保存调用
xf = x.f
while True:
print(xf())
上面调用 x.f() 时并没有带参数,但是类定义中 f() 的函数定义指定了一个参数。
方法的特殊之处就在于 实例对象 会作为函数的第一个参数被传入(即调用 x.f() 其实就相当于 MyClass.f(x))
总之,调用一个具有 n 个参数的方法就相当于调用 n+1 个参数的对应函数,实例对象为第一个参数值。
2.5 类变量和实例变量
一般来说,实例变量用于每个实例的唯一数据,而类变量用于类的所有实例共享的属性和方法:
class Dog:
kind = 'canine' # 类变量
def __init__(self, name):
self.name = name # 实例变量
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.kind # d,e共享
'canine'
>>> e.kind
'canine'
>>> d.name # d私有
'Fido'
>>> e.name # e私有
'Buddy'
2.5.1 类变量共享(避免使用)
class Dog:
tricks = [] # 类变量
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # tricks[]被所有实例对象共享改动
['roll over', 'play dead']
2.5.2 实例变量私有(通常用法)
class Dog:
def __init__(self, name):
self.name = name
self.tricks = [] # 在类实例化后创建一个实例变量
def add_trick(self, trick):
self.tricks.append(trick)
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over')
>>> e.add_trick('play dead')
>>> d.tricks # d私有一个tricks列表
['roll over']
>>> e.tricks # e私有一个tricks列表
['play dead']
方法可以通过使用 self 参数的方法属性调用其他方法:(通过self隐形传递,参见上2.1.2 类方法)
class Bag:
def __init__(self):
self.data = []
def add(self, x):
self.data.append(x)
def addtwice(self, x):
self.add(x)
self.add(x)
每个值都是一个对象,因此具有 类 (也称为 类型),并存储为 object.__class__ 。(类似用type(object))
3 继承
class DerivedClassName(BaseClassName):
或者 该基类来源自其他模块
class DerivedClassName(modname.BaseClassName):
如果请求的属性在 派生类 中找不到,搜索将转往 基类 中进行查找。 如果基类本身也派生自其他某个类,则此规则将被递归地应用。
派生类的实例化(与类实例化无区别): DerivedClassName() 会创建该类的一个新实例。
Python有两个内置函数可被用于继承机制:
- 使用 isinstance() 来检查一个实例的类型: isinstance(obj, int) 仅会在 obj.__class__ 为 int 或某个派生自 int 的类时为 True。
- 使用 issubclass() 来检查类的继承关系: issubclass(bool, int) 为 True,因为 bool 是 int 的子类。 但是,issubclass(float, int) 为 False,因为 float 不是 int 的子类。
4 多重继承
class DerivedClassName(Base1, Base2, Base3):
<statement-1>
.
.
.
<statement-N>
按照深度优先,从左至右的 静态 假设下:如果某一属性在 DerivedClassName 中未找到,则会到 Base1 中搜索它,然后(递归地)到 Base1 的基类中搜索,如果在那里未找到,再到 Base2 中搜索,依此类推。
4.1 父类调用
super() 函数是用于调用父类(超类)的一个方法。super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。(MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。)
4.2 super() 功能
- 方法扩展
- 隔离更改
内置类的 方法扩展 示例:
class LoggingDict(dict):
def __setitem__(self, key, value):
logging.info('Settingto %r' % (key, value))
super().__setitem__(key, value)
此类具有与其父项(dic)相同的功能,但它扩展了 [setitem] 方法,以在更新密钥时创建日志条目。创建日志条目后,该方法使用 super() 来委派使用键/值对实际更新字典的工作。
在 super() 之前,我们将用dict._setitem_(self, key, value)硬连接。但是,super() 更好,因为它是间接引用。间接的一个好处是 隔离更改 ,我们不必按名称指定 父类 。如果需要变动基类,则 super() 引用将自动跟随不用改动。
class LoggingDict(SomeOtherMapping): # 新的基类
def __setitem__(self, key, value):
logging.info('Settingto %r' % (key, value))
super().__setitem__(key, value) # 不用改动
实例研究:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class FooParent(object):
def __init__(self):
self.parent = 'I\'m the parent.'
print ('Parent')
def bar(self,message):
print ("%s from Parent" % message)
class FooChild(FooParent):
def __init__(self):
super(FooChild,self).__init__()
#等同super().__init__()
print ('Child')
def bar(self,message):
super(FooChild, self).bar(message)
#等同super().bar(message)
print ('Child bar fuction')
print (self.parent)
if __name__ == '__main__':
fooparent = FooParent()
fooChild = FooChild()
fooChild.bar('HelloWorld')
fooparent = FooParent()实例化
Parent
一个__init__函数初始化后的实例对象,没有__init__函数则为空的实例对象
fooChild = FooChild() 实例化
Parent
Child
类似上面dict.__setitem__方法的扩展,该super(FooChild,self).__init__() 是对FooParent.__init__()的扩展,因此输出两个值。
fooChild.bar('HelloWorld') 属性引用
HelloWorld from Parent
Child bar fuction
I’m the parent.
同样的使用super().bar(message) 对FooParent.bar()方法的扩展。
super() 扩展:
函数的装饰器(由方法到函数)
装饰器仍然是一个 Python 函数,实现由 闭包 (不用改动)支撑,装饰器的返回值也是一个 函数对象。让函数在无需修改任何代码的前提下给其增加功能(类似super())。
def debug(func):
#定义外层 debug(func),return wrapper,然后定义内层wrapper(),return func
def wrapper():
#增加一些额外的功能
#……
print("[DEBUG]: enter {}()".format(func.__name__))
#……
return func()
#执行额外的功能,再执行func----相当于被装饰func多了这些额外的功能
return wrapper
@debug
def say_hello():
print("hello!")
常见功能
- 计算函数的运行时间
- 计算函数的运行次数
- 给函数插入运行日志
- 让函数实现事务一致性:让函数要么一起运行成功,要么一起运行失败
- 实现缓存处理
- 权限校验:在函数外层套上权限校验的代码,实现权限校验
计算函数运行时间示例:
def timer(func):
def wrapper(*args, **kwargs): #因为被装饰sum()有多个参数
start_time = time.time()
res = func(*args, **kwargs)
print("[Time out]: %.4f s" % (time.time() - start_time))
return res
return wrapper
#timer装饰器,在运行sum()时,同时计算其运行时间,与sum()函数无关。
@timer
def sum(a, b):
time.sleep(2)
return a + b
print("计算结果:", sum(1, 2))
# 运行结果:
[Time out]: 2.0019 s
计算结果: 3
计算函数运行次数示例:
import logging
counts= 0
def count(f):
def wrapper(*args,**kwargs):
global counts
counts += 1
result = f(*args,**kwargs)
logging.warning("%s processed %s times!"%(f.__name__,counts))
return result
return wrapper
@count
def hello(s):
print(s)
hello("well")
hello("Hello!")
hello("Word!")
#输出
WARNING:root:hello processed 1 times!
WARNING:root:hello processed 2 times!
WARNING:root:hello processed 3 times!
well
Hello!
Word!
运行日志装饰器:
def trace_func(func):
def tmp(*args, **kargs):
#*args 可以传入元组和列表,**kargs可以传入字典,两者都是可选参数
print('Start %s(%s, %s)...' % (func.__name__, args, kargs) )
return func(*args, **kargs)
return tmp
@trace_func
def log_test_with_empty_parameter():
pass
@trace_func
def log_test_with_many_parameter(a_int, b_string, c_list, d_dict):
pass
@trace_func
def log_test_with_key_parameter(a = 'www', b = 1, c = [1,2]):
pass
if __name__ == '__main__':
log_test_with_empty_parameter()
log_test_with_many_parameter(1, 'wwww', [1,2,'c'], {1: 'a', 2 : 'ww'})
log_test_with_key_parameter(1, 'wwww', c = [3, 4])
#运行结果
Start log_test_with_empty_parameter((), {})...
Start log_test_with_many_parameter((1, 'wwww', [1, 2, 'c'], {1: 'a', 2: 'ww'}), {})...
Start log_test_with_key_parameter((1, 'wwww'), {'c': [3, 4]})...
权限校验装饰器:
user_list = [
{'name':'user1','passwd':'123'},
{'name':'user2','passwd':'123'},
{'name':'user3','passwd':'123'},
]
#初始状态,用来保存登陆的用户,
client_dic = {'username':None,'login':False}
#添加新功能
def auth_func(func):
def wrapper(*args,**kwargs):
#参数检查,判断是否有用户登录,如果有不用验证,直接执行函数的功能
if client_dic['username'] and client_dic['login']:
res = func(*args,**kwargs)
return res
username = input('用户名:').strip()
passwd = input('passwd:').strip()
#对比列表,检查用户名和密码是否正确
for user_dic in user_list:
if username == user_dic['name'] and passwd == user_dic['passwd']:
client_dic['username'] = user_dic['name']
client_dic['login'] = True
res = func(*args,**kwargs)
return res
else:
print('用户名或者密码错误!')
return wrapper
@auth_func
def index():
print("欢迎来到主页")
@auth_func
def home(name):
print("欢迎回家:%s"%name)
@auth_func
def shoppping_car():
print('购物车里有[%s,%s,%s]'%('面包','可乐','薯片'))
index()
home('root')
shoppping_car()
参考资料:https://rhettinger.wordpress.com/
https://docs.python.org/zh-cn/3/tutorial/classes.html
https://www.runoob.com/python/python-func-super.html
http://www.python88.com/topic/148/
https://www.cnblogs.com/junneyang/p/5332307.html
https://blog.csdn.net/qq_33531400/article/details/79324551

下次再见