In [7]:
class Circle(object):
PI = 3.14 #类变量
def __init__(self,radius):
self.radius = radius #实例变量
def get_areas(self):
return PI * self.radius * self.radius
mycircle = Circle(2) #实例化
print(mycircle.radius) # 实例变量
print(mycircle.PI) #类变量
print(Circle.PI) #也可以使用类名直接调用类变量
当访问mycircle.radius,实际上是通过字典 mycircle.dict 查看相应的值。但是,如果回到类这一层,属性也是存储在类的字典里面:
In [8]:
Circle.__dict__
Out[8]:
In [9]:
# 如果加上继承呢?
class Widget(object):
copyright = 'witrett, inc.'
class Circle(Widget):
PI = 3.14
# copyright = 'circle copyright'
def __init__(self, radius):
self.radius = radius
mycircle = Circle(2)
print(type(mycircle).mro())
# mro即method resolution order,主要用于在多继承时判断调的属性的路径(来自于哪个类)。后续会进一步介绍
print(mycircle.copyright) #显然,这里是使用Widget中的变量copyright
In [10]:
Circle.__dict__
Out[10]:
也就是说,在查找属性或者方法的时候,会递归的查找mro中的内容,直到找到一个匹配项。
在传统的Python编码中,我们可以用下面的方式破坏面向对象的封装性:
In [11]:
class Widget(object):
copyright = 'witrett, inc.'
class Circle(Widget):
PI = 3.14
def __init__(self, radius):
self.radius = radius
self.circumference = 2 * self.radius * self.PI
mycircle = Circle(2)
mycircle.radius = 3
mycircle.circumference #呵呵,修改并没生效
Out[11]:
可以看到,虽然尝试使用mycircle.radius = 3这个语句尝试改变实例属性,但是并没有成功
In [12]:
# 尝试一下如何改变这一切:
class Circle(Widget):
PI = 3.14
def __init__(self, radius):
self.radius = radius
@property
def circumference(self):
return 2 * self.radius * self.PI #现在ok
mycircle = Circle(2)
mycircle.radius = 3
mycircle.circumference #呵呵,属性修改成功
Out[12]:
于是,当我们按照这样的方式obj.foo在对象上访问一个属性时,就可以得到如下所示的一些简单的规则:
MRO,主要用于在多继承时判断所调用属性的路径。
本地优先级:指声明时父类的顺序,比如C(A,B),如果访问C类对象属性时,应该根据声明顺序,优先查找A类,然后再查找B类。 单调性:如果在C的解析顺序中,A排在B的前面,那么在C的所有子类里,也必须满足这个顺序。
In [22]:
def c3_lineration(kls):
if len(kls.__bases__) == 1:
return [kls, kls.__base__]
else:
l = [c3_lineration(base) for base in kls.__bases__]
l.append([base for base in kls.__bases__])
return [kls] + merge(l)
def merge(args):
if args:
for mro_list in args:
for class_type in mro_list:
for comp_list in args:
if class_type in comp_list[1:]:
break
else:
next_merge_list = []
for arg in args:
if class_type in arg:
arg.remove(class_type)
if arg:
next_merge_list.append(arg)
else:
next_merge_list.append(arg)
return [class_type] + merge(next_merge_list)
else:
raise Exception
else:
return []
class A(object):pass
class B(object):pass
class C(object):pass
class E(A,B):pass
class F(B,C):pass
class G(E,F):pass
print(c3_lineration(G))
其实只需用知道以下的规则即可:
mro(G) = [G] + merge(mro[E], mro[F], [E,F]) = [G] + merge([E,A,B,O], [F,B,C,O], [E,F]) = [G,E] + merge([A,B,O], [F,B,C,O], [F]) = [G,E,A] + merge([B,O], [F,B,C,O], [F]) = [G,E,A,F] + merge([B,O], [B,C,O]) = [G,E,A,F,B] + merge([O], [C,O]) = [G,E,A,F,B,C] + merge([O], [O]) = [G,E,A,F,B,C,O]
In [ ]:
# 这个例子尝试给他小伙伴们解释getattr和__getattr__的使用:虽然类wrapper没有定义append方法,但是可以通过getattr调用list的append方法。
In [13]:
class wrapper:
def __init__(self, object):
self.wrapped = object
def __getattr__(self, attrname):
print('Trace:', attrname)
return getattr(self.wrapped, attrname)
x = wrapper([1,2,3])
x.append(4)
In [5]:
x
Out[5]:
In [14]:
x.wrapped
Out[14]:
这个示例有两个类,cook和material:
In [15]:
class cook:
def __init__(self, material='rice'):
self.material = material
def boil(self):
print('had boiled ', self.material)
class material:
def __init__(self, rice):
self.rice = rice
self.meth = cook()
def clean(self):
print('clean up first')
def __getattr__(self, attr):#通过这样的方式,一旦material的实例属性找不到需要的实例变量活着实例方法,就从cook类中寻找
return getattr(self.meth, attr)
m = material('rice')
m.boil()# 注意此时m是material类的实例,但是boil方法属于cook类,就是通过上面的getattr语句连接起来
原本material和cook类之间的关系仅仅只是在material类的‘构造’函数中,实例化了cook类,一旦在material的实例中没有找到需要的方法,就自动加载cook类中的方法。
In [16]:
m.meth.__dict__
Out[16]:
如果小伙伴想要更进一步了解软件开发的知识,设计模式是没有办法回避的主题。本次培训只给大家介绍两个常见的示例:
抽象工厂有一个优点,在使用工厂方法时从用户视角通常是看不到的,那就是抽象工厂能够通过改变激活的工厂方法动态地(运行时)改变应用行为。一个经典例子是能够让用户在使用应用时改变应用的观感(比如,Apple风格和Windows风格等),而不需要终止应用然后重新启动。
In [28]:
class Frog:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, obstacle):
print('{} the Frog encounters {} and {}!'.format(self,
obstacle, obstacle.action()))
class Bug:
def __str__(self):
return 'a bug'
def action(self):
return 'eats it'
class FrogWorld:
def __init__(self, name):
print(self)
self.player_name = name
def __str__(self):
return '\n\n\t------ Frog World -------'
def make_character(self):
return Frog(self.player_name)
def make_obstacle(self):
return Bug()
######################### 分割线,两个不同的两组类 #############################
class Wizard:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, obstacle):
print(
'{} the Wizard battles against {} and {}!'.format(
self,
obstacle,
obstacle.action()))
class Ork:
def __str__(self):
return 'an evil ork'
def action(self):
return 'kills it'
class WizardWorld:
def __init__(self, name):
print(self)
self.player_name = name
def __str__(self):
return '\n\n\t------ Wizard World -------'
def make_character(self):
return Wizard(self.player_name)
def make_obstacle(self):
return Ork()
class GameEnvironment:
def __init__(self, factory):
self.hero = factory.make_character()
self.obstacle = factory.make_obstacle()
def play(self):
self.hero.interact_with(self.obstacle)
######################### 以上是两组类的不同实现 ###############################
def validate_age(name):
try:
age = input('Welcome {}. How old are you? '.format(name))
age = int(age)
except ValueError as err:
print("Age {} is invalid, please try again...".format(age))
return (False, age)
return (True, age)
def main():
name = input("Hello. What's your name? ")
valid_input = False
while not valid_input:
valid_input, age = validate_age(name) # 验证判断输入是否正确,而且判断输入的年龄
game = FrogWorld if age < 18 else WizardWorld# 通过年龄判断应该使用Frogworld还是wizardworld
environment = GameEnvironment(game(name))
environment.play()
main()
# Hello. What's your name? Nick
# Welcome Nick. How old are you? 17
# ------ Frog World -------
# Nick the Frog encounters a bug and eats it!
本示例中,判断输入的年龄来选择实例化frogworld还是wizardworld,然后直接调用play方法。
策略模式(Strategy pattern)鼓励使用多种算法来解决一个问题,其杀手级特性是能够在运行时透明地切换算法(客户端代码对变化尤感知)。因此,如果你有两种算法,并且知道其中一种对少量输入效果更好,另一种对大量输入效果更好,则可以使用策略模式在运行时基于输入数据决定使用哪种算法。
In [54]:
import types
class StrategyExample:
def __init__(self, func=None):
self.name = 'Strategy Example 0'
if func is not None:
self.execute = types.MethodType(func, self)# MethodType: The type of methods of user-defined class instances.
print(self.execute)
def execute(self):
print(self.name)
def execute_replacement1(self):
print(self.name + ' from execute 1')
def execute_replacement2(self):
print(self.name + ' from execute 2')
if __name__ == '__main__':
strat0 = StrategyExample()
strat1 = StrategyExample(execute_replacement1)
strat1.name = 'Strategy Example 1'
strat2 = StrategyExample(execute_replacement2)
strat2.name = 'Strategy Example 2'
strat0.execute()
strat1.execute()
strat2.execute()
可以看到,这两个例子都是非常简单的,设计模式就是这样:完全都是经验的总结。使用python实现设计模式尤其简单,很多时候会发现自己组织实现了几个类,就是用到了好几种模式。如果小伙伴没有计划从事软件开发,不掌握也没有关系,反之,则必须要有所了解。
Global Interpreter lock: Python解释器在同一时刻只能运行在一个线程之中。关于这方面的内容,还是参考David beazley相关的演讲:http://www.dabeaz.com/talks.html
In [ ]:
import threading
def worker(num):
"""thread worker function"""
print('Worker: %s' % num)
threads = []
for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
对于多线程的问题,基本可以到此为止了,因为实在是不推荐使用,因为里面需要了解的东西实在太多了,一不小心,反而性能更糟糕。
In [ ]:
装饰器是一种特殊的函数,实现的细节非常套路,但是一旦用好了,可以大大简化程序的设计,减少很多重复代码,并且使功能增强
In [25]:
import time
from functools import wraps
def timethis(func):
'''
Decorator that reports the execution time.
'''
#@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
return wrapper
注意上面的示例中使用了标准库functools中自带的wraps方法作为装饰器,用来装饰wrapper函数,这样做可以保留被装饰函数中的元信息
In [26]:
@timethis
def countdown(n):
'''
Counts down
'''
while n > 0:
n -= 1
countdown(100000)
In [27]:
countdown.__name__
Out[27]:
In [28]:
countdown.__doc__
In [29]:
countdown.__annotations__
Out[29]:
一个装饰器就是一个函数,它接受一个函数作为参数并返回一个新的函数。
In [30]:
@timethis
def countdown(n):
pass
跟像下面这样写其实效果是一样的:
In [31]:
def countdown(n):
pass
countdown = timethis(countdown)
In [44]:
import types
import time
from functools import wraps
class thistime:
def __init__(self, func):
wraps(func)(self)
self.ncalls = 0
def __call__(self, *args, **kwargs):# 通过这里实现装饰器的功能
self.ncalls += 1
start = time.time()
f = self.__wrapped__(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return f
def __get__(self, instance, cls):
if instance is None:
return self
else:
return types.MethodType(self, instance)
In [ ]:
# 这里实现类装饰器的关键在于 __call__ 函数,如果小伙伴觉得这种写法难以理解,也可以不用这样写,毕竟python还是有一定的灵活度
In [45]:
@thistime
def countdown(n):
while n > 0:
n -= 1
countdown(100)
In [32]:
import time
from functools import wraps
def timethis(func):
'''
Decorator that reports the execution time.
'''
@wraps(func)## 最好默认都使用这个wraps
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
return wrapper
In [33]:
@timethis
def countdown(n):
'''
Counts down
'''
while n > 0:
n -= 1
countdown(100000)
In [34]:
countdown.__name__
Out[34]:
In [35]:
countdown.__doc__
Out[35]:
In [36]:
countdown.__annotations__
Out[36]:
In [37]:
from functools import wraps
import logging
def logged(level, name=None, message=None):
"""
Add logging to a function. level is the logging
level, name is the logger name, and message is the
log message. If name and message aren't specified,
they default to the function's module and name.
"""
def decorate(func):
logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__
@wraps(func)
def wrapper(*args, **kwargs):
log.log(level, logmsg)
return func(*args, **kwargs)
return wrapper
return decorate
# Example use
@logged(logging.CRITICAL)
def add(x, y):
return x + y
@logged(logging.CRITICAL, 'example', 'this is very important')
def spam_func():
print('Spam!')
初看起来,这种实现看上去很复杂,但是核心思想很简单。 最外层的函数 logged() 接受参数并将它们作用在内部的装饰器函数上面。 内层的函数 decorate() 接受一个函数作为参数,然后在函数上面放置一个包装器。 这里的关键点是包装器是可以使用传递给 logged() 的参数的。
In [38]:
add(3,4)
Out[38]:
In [39]:
spam_func()
In [42]:
@decorator(x, y, z)
def func(a, b):
pass
装饰器处理过程跟下面的调用是等效的:
In [41]:
def func(a, b):
pass
func = decorator(x, y, z)(func)
装饰器是属于比较进阶的Python知识了,如果小伙伴不是想要从事python开发的工作,这个可以不用掌握,但是还是需要了解这个语法在做什么。
In [5]:
import time
from functools import wraps
# A simple decorator
def timethis(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
r = func(*args, **kwargs)
end = time.time()
print(end-start)
return r
return wrapper
# Class illustrating application of the decorator to different kinds of methods
class Spam:
@timethis
def instance_method(self, n):
print(self, n)
while n > 0:
n -= 1
@classmethod
@timethis
def class_method(cls, n):
print(cls, n)
while n > 0:
n -= 1
@staticmethod
@timethis
def static_method(n):
print(n)
while n > 0:
n -= 1
@classmethod 和 @staticmethod 实际上并不会创建可直接调用的对象, 而是创建特殊的描述器对象
In [6]:
s = Spam()
s.instance_method(10)
In [9]:
Spam.class_method(1000) #classmethod 可以是直接通过类名称调用
In [8]:
Spam.static_method(1000) #严格的讲,staticmethod方法和该方法所在的类没有什么关系,只是为了代码管理方便才放到一起
In [61]:
s.class_method(11)
还有一点就是要注意装饰器的叠加,其实这个也很好理解,就是下面的装饰器再被上面的装饰器函数执行一遍
还有一点非常重要:在实际的项目中,装饰器的定义一定要在一个单独的py文件中。