python基础-面向对象(装饰器)

属性:
  @property
  @method_name.setter
  @method_name.deleter
  三个标签都是放在方法的上面来使用,且方法名要和后续使用的
  变量名字相一致。
  
  好处:
  1 防止别人乱改变量内容
  2 在操作变量的时候,做一些附加操作,比如:写日志、写数据库
    做参数的相关计算。

4 私有变量和私有方法:
  变量和方法前面有2个下划线。
  私有的仅限类内部使用,不能被类外部调用(不太严格)。
  私有变量和私有方法可以被继承么?不行

5 继承:
  父类、子类、子类继承父类
  构造方法:
  1 子类没有构造方法,调用父类的构造方法,如果父类的构造方法有
  参数的话,那么实例化的时候也需要传递对应个数的参数。
  2 如果子类有构造方法,父类的构造方法此时不会被调用。
    父类构造方法有参数,那么你调用未初始化的父类实例变量会出错
    推荐子类有构造方法的时候,显式调用父类的构造方法

多重继承:
  简单可以继承多个父类的方法和变量
  1 子类没有构造方法:调用第一个父类的构造方法,那么实例化的时候也需要传递对应个数的参数。
  2 子类有构造方法:建议:所有的父类都实例化

方法重写:
  在子类中重新定义父类中的同名方法。
  简单来说:改造父类中不适合子类的部分。

  1.   新式类和经典类啥区别:
      新式类继承的时候需要使用(object)
      @method_name.setter
      @method_name.deletter
      支持新式类,不支持经典类
      @property 支持新式类和经典类
  2. 封装:
  3. 把变量值通过实例化的方式传入到实例中来使用。
  4. 通过类中的方法(3种:类方法、实例方法、静态方法)把操作数据的逻辑进行了封装。

with open("e:\\a.txt".r) as fp:
    fp.readline()
    fp.read()
    fp.seek()

推荐大家使用类似的封装方法。

  1. 多态

通过调用一个方法,呈现出不同的结果。某个对象中存在重名函数,就能实现多态。

class F1:
    pass

class S1(F1):

def show(self):
        print 'S1.show'

class S2(F1):

def show(self):
        print 'S2.show'

def Func(obj):
    """Func函数需要接收一个F1类型或者F1子类的类型"""
    
    obj.show()
    
s1_obj = S1()
Func(s1_obj) # 在Func函数中传入S1类的对象 #s1_obj,执行 S1 的show方法,结果:#S1.show

s2_obj = S2()
Func(s2_obj) # 在Func函数中传入Ss类的对象 #ss_obj,执行 Ss 的show方法,结果:#S2.show

跟基类没有关系,java存在类型,必须是基类和子类的继承关系才会存在多态;python不存在类型,所以跟基类也没有关系,只要传递的对象中有该方法就可以。

class F1:

pass

class S1(F1):

def show(self):

print 'S1.show'

class S2(F1):

def show(self):

print 'S2.show'

class X:

def show(self):

print 'x.show'  #python中不必须是继承关系,只要有这个重名的函数就可以

def Func(obj):

"""Func函数需要接收一个F1类型或者F1子类的类型"""

obj.show()

s1_obj = S1()

Func(s1_obj) # 在Func函数中传入S1类的对象 #s1_obj,执行 S1 的show方法,结果:

#S1.show

s2_obj = S2()

Func(s2_obj) # 在Func函数中传入Ss类的对象 #ss_obj,执行 Ss 的show方法,结果:

#S2.show

x_obj=X()

Func(x_obj)

  1. 多态意味着可以对不同的对象使用同样的操作,但它们可能会以多种形态呈现出结果。在Python中,任何不知道对象到底是什么类型,但又需要对象做点什么的时候,都会用到多态

#coding=utf-8

class calculator:

def count(self,args):

return 1

calc=calculator()

#自定义类型

from random import choice

#obj是随机返回的类型不确定

obj=choice(['hello,world',[1,2,3],calc])

print obj

print type(obj)

print obj.count('a') #方法多态

#obj取到calc时等价于执行calc.count('a')

#如果没有取到calc,会自动执行字符串和列表自有的count()方法,由于

['hello,world',[1,2,3]里面都没有a,所以打印出来了0

  1. 多态—” 鸭子类型

#coding=utf-8

class Duck(object):

def quack(self):

print "Quaaaaaack!"

def feathers(self):

print "The duck has white and gray feathers."

class Person(object):

def quack(self):

print "The person imitates a duck."

def feathers(self):

print "The person takes a feather from the ground and shows it."

def in_the_forest(duck):   #必须有这么一个方法,能有接收不同类型的参数,这个方法里面的参数都是一样的。

duck.quack()

duck.feathers()

donald = Duck()

john = Person()

in_the_forest(donald)

in_the_forest(john)

多态的好处:去除冗余代码

有一个方法可以接受不同类型的参数,产生不同的效果。

  1. 多态运算符多态

#coding=utf-8

def add(x,y):

return x+y

print add(1,2)  #输出3

print add("hello,","world")  #输出hello,world

print add(1,"abc") #将抛出异常 TypeError,不同类型不能相加

  1. 方法重载

如果你学过C++、 JAVA或者是其他面向对象编程语言,你会发现Python中的重载与其他面向对象编程语言在使用上是有区别,其实可以这么说Python在形式上是不支持重载。

有相同的方法名时也能调用到最后一个,因此它是不支持方法重载的。

在java和c中,方法是可以重载的,方法名相同参数个数不同。

  1. 方法重写

重写是指子类重写父类的成员方法。子类可以改变父类方法所实现的功能,但子类中重写的方法必须与父类中对应的方法具有相同的方法名。也就是说要实现重写,就必须存在继承。
简单来讲,就是如果父类的方法不能满足子类的需求,子类就可以重写从父类那继承过来的方法。

方法重写必须方法名相同、参数个数相同

#coding=utf-8

class Parent(object):  # 定义父类
  def myMethod(self):
    print 'call Parent'

def printName(self):
    print "my name is LiLy"

class Child(Parent): # 定义子类
  def myMethod(self): # 子类重写父类myMethod方法
    print 'call Child'
    
    Parent.myMethod(self)
c = Child()  # 子类实例
c.myMethod() # 子类调用重写方法
c.printName()

  1. 运算符重写

#coding=utf-8

class Vector(object) :

def __init__(self, a, b) :

self.a = a

self.b = b

print self.a,self.b

def __str__(self):

print self.a,self.b

return '----Vector (%d, %d)' % (self.a, self.b)

def __add__(self,other) :

#return Vector(self.a + other.a, self.b + other.b) #生成新的实例,print打印

时会固定的调用str这个方法

return self.a + other.a, self.b + other.b  #直接这样不做实例化就不会调用str方法了

x =  Vector(3,7)

y =  Vector(1, -10)

print x + y

#print str(x)

#实现两个对象相加:3+1,7+-10

  1. __call__
    对象后面加括号,触发执行

class Foo:

def __init__(self):

pass

def __call__(self, *args, **kwargs):

print '__call__'

obj = Foo() # 执行 __init__

obj()       # 执行 __call__

  1. 单例

实例只有一个,此为单例;占用的都是同一块内存。因为__new__方法的存在而实现的

#方法1,实现__new__方法
#并在将一个类的实例绑定到类变量_instance上,
#如果cls._instance为None说明该类还没有实例化过,实例化该类,并返回
#如果cls._instance不为None,直接返回

class Singleton(object):

def __new__(cls, *args, **kw):  #重写是为了控制它只生成一个

if not hasattr(cls, '_instance'):

orig = super(Singleton, cls) #调用父类

cls._instance = orig.__new__(cls, *args, **kw) #生成实例

return cls._instance

class MyClass(Singleton):

a = 1

one = MyClass()

two = MyClass()

two.a = 3

print one.a

#3

#one和two完全相同,可以用id(), ==, is检测

print id(one)

#29097904

print id(two)

#29097904

print one == two

#True

print one is two

#True

  1. python对象销毁(垃圾回收)

内存泄漏:内存一直不释放就会出现内存泄漏,功能测试是很难发现的,操作次数少

系统连接数达不到那么多

这些必须从稳定性测试去测

循环引用:

  1. 装饰器

在代码运行期间在不改变原函数定义的基础上,动态给该函数增加功能的方式,称之为装饰器(Decorator)。

1)变量的工作范围

#coding=utf-8

outerVar = "this is a global variable"

def test() :

innerVar = "this is a Local variable"

#outerVar+=1  #发生赋值时就会报错,因为这个变量没有定义为全局,作用域有限

print outerVar #不赋值是不会报错的

print n

n = 10

test()

变量生存空间

#coding=utf-8

outerVar = "this is a global variable"

def test() :

innerVar = "this is a Local variable"

test()

print innerVar

嵌套函数

#coding=utf-8

def outer() :

name = "python"

def inner() :

print name

return inner()

outer()

print outer()

函数作为变量

#coding=utf-8

def add(x, y):

return x + y

def sub(x, y):

return x - y

def apply(func, x, y):

return func(x, y)

print apply(add, 2, 1)

print apply(sub, 2, 1)

包:内置函数带有函数外的参数,在外部执行

函数对象:就是指向函数指向的内存中的那部分地址

#coding=utf-8

def outer() :
  name = 1
  def inner() :
    print name   红色字体部分组成一个闭包
  return inner  #函数对象

res = outer()
print type(res)
res()    #闭包:内置函数带有函数外的参数,在外部执行
print res.func_closure #打印一下闭包里面的变量

装饰器:装饰器其实就是一个闭包,把一个函数当做参数后返回一个替代版函数。

如果我们想给now()函数增加一些别的功能,比如在调用该函数前后自动打印一些日志,但又不希望修改原now()的定义,这时候我们的装饰器就配上用场了。

#coding=utf-8

import time

#定义装饰器

def log(func):

def wrapper(*args, **kw):

print 'call func is %s' %func.__name__

return func(*args, **kw)   #黄色字体部分形成一个闭包

#*args, **kw 可以接受任参数作为函数对象

return wrapper

@log

def now():    # @log+def now()  等价于log(now)

now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

print "current time is %s" %now

now()   #加括号是为了调用函数,等价于执行wrapper

print now  #打印出now是个函数

log(now)  #等价于wrapper

执行顺序:

now --->log(now)---->返回一个闭包:带有一个变量是函数对象now,还有wrapper这个嵌套函数----》wrapper()---》显示结果:打印了一下调用函数的名字和now函数的执行结果

使用@将装饰器应用到函数

理解过程:

#coding=utf-8

import time

#定义装饰器

def log(func):

def wrapper(*args, **kw):

print 'call func is %s' %func.__name__

return func(*args, **kw)

return wrapper

def now():

now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

print "current time is %s" %now

#now是一个函数对象,传递到log中

#log函数的func局部变量值变成了now这个函数对象

#执行log函数,返回的是一个闭包-->闭包是啥?是wrapper这个嵌套函数+外部变量func

组成的,func现在是啥?func现在等于now函数

#print log(now)

#打印结果:<function wrapper at 0x00000000026DDB38>

#此结果返回了一个wrapper函数对象;证明log(now)返回的是一个函数

#如何调用函数? log(now)() 加括号就可以调用

log(now)()

#call func is now

#current time is 2017-09-24 17:26:11

#从结果可以看到,已经成功调用了该函数

#那么log(now)的另外一种写法是什么呢? 装饰器@log +def now()

import time

#定义装饰器

def log(func):

def wrapper(*args, **kw):

print 'call func is %s' %func.__name__

return func(*args, **kw)

return wrapper

@log

def now():

now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

print "current time is %s" %now

#那么log(now)的另外一种写法是什么呢? 装饰器@log +def now(),把代码改成了这种形

#式了,那么调用就可以简化了呗

now()

#call func is now

#current time is 2017-09-24 17:29:17

#call func is now

#current time is 2017-09-24 17:29:17

#可以看到两个的执行结果是完全一样的。

小练习:写一个装饰器,把这个函数的执行时间计算出来。

#coding=utf-8

import time

#定义装饰器

def time_cal(func):

def time_cal2(*args, **kw):

start_time = time.time()

func(*args, **kw)

end_time = time.time()

print end_time-start_time

#return func(*args, **kw) #返回一下这个函数的执行结果

return time_cal2 #返回结果是一个函数对象,是这歌函数的执行结果

@time_cal

def now():

now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

print "current time is %s" %now

time.sleep(5)  #直接打印时间过快,加个停顿可以看出来时间,不然会打印出来0.0

now()

第三步:

#coding=utf-8

def deco(func):

print "before myfunc() called."

func()

print "  after myfunc() called."

return func

@deco

def myfunc():

print " myfunc() called."

#myfunc()

#myfunc()

#E:\>python c.py

#before myfunc() called.

# myfunc() called.

#  after myfunc() called.

#注释掉了调用函数,发现还是打印出来了,这就是装饰器的作用 等价于deco(myfounc

第四步:

def deco(func):
    def _deco():
        print "before myfunc() called."
        func()
        print "  after myfunc() called."
        # 不需要返回func,实际上应返回原函数的返回值
    return _deco

@deco
def myfunc():
    print " myfunc() called."
    return 'ok'

@deco
def print_mesage():
    print "my message"

print myfunc  #返回闭包:由_deco和myfunc的函数对象组成
myfunc()  #调用了_deco()函数
print_mesage()

装饰器:减少重复代码的编写

装饰器的四步法:

  1. 最简单的函数,准备附加额外功能。

打印两次

#conding = utf-8

def myfunc():

print "myfunc() called"

myfunc()

myfunc()

  1. 使用装饰函数在函数执行前和执行后分别附加额外功能

装饰函数的参数是被装饰的函数对象,返回原函数对象

装饰的实质语句: myfunc = deco(myfunc)'''

#conding = utf-8

def deco(func):

print "before myfunc() called"

func()

print "after myfunc() called"

#print func

return func

def myfunc():

print "myfunc() called"

myfunc = deco(myfunc)

#以这样的形式去调用时,不加入return func会报错'NoneType' object is not callable

myfunc()

myfunc()

  1. 使用语法糖@来装饰函数,相当于“ myfunc = deco(myfunc)”但发现新函数只在第一次被调用,且原函数多调用了一次。等价于第二步程序

#conding = utf-8

def deco(func):

print "before myfunc() called"

func()

print "after myfunc() called"

#print func

return func

@deco

def myfunc():

print "myfunc() called"

#myfunc = deco(myfunc)    #@@deco这;两种方式是等价的

myfunc()

myfunc()

调用了两次,打出来三次,而且后两次的结果都不符合预期。

  1. 使用内嵌包装函数来确保每次新函数都被调用

#conding = utf-8

def deco(func):

def _deco():  #使用内嵌包装函数来确保每次新函数都被调用

print "before myfunc() called"

func()

print "after myfunc() called"

#return func #不需要返回func,实际上应该返回的是原函数的返回值

return _deco #内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象

@deco

def myfunc():

print "myfunc() called"

myfunc()

myfunc()

关于装饰器自己的分析过程:

#conding = utf-8

def deco(func):

print "before myfunc() called"

func()

print "after myfunc() called"

#print func

#return func

@deco

def myfunc():

print "myfunc() called"

#myfunc = deco(myfunc)    #@deco这;两种方式是等价的

myfunc()

#myfunc()

#@deco

#def myfunc():  等价于 myfunc = deco(myfunc)

#这句话的意思是 利用@deco呼叫deco这个函数,并且赋值给被@的函数名即myfunc

#执行 步骤:

#第一轮 (deco def myfunc():    print "myfunc() called")

#为啥没调用也会执行? 因为这就是@deco这个在起作用

#1.myfunc = deco(myfunc)

#2.print "before myfunc() called"

#3.print "myfunc() called"

#4.print "after myfunc() called"

#5.没有return,默认返回一个 none

#此时myfunc等于none了  为啥等于none?因为deco(func)作为参数被传进来的,你

#不做返回它会自动返回none

#第二轮: 执行myfunc()

#上一轮执行后 myfunc成了none,后续无法执行了,报错 TypeError: 'NoneType' #object is not callable

借鉴之处:https://segmentfault.com/q/1010000006990592

上一篇:hdu2848 Visible Trees (容斥原理)


下一篇:python基础-5.2装饰器