python 高级编程

1.1==,is的使用

·is是比较两个引用是否指向了同一个对象(引用比较)。

·==是比较两个对象是否相等。

1.2深拷贝、浅拷贝

1.2.1浅拷贝

浅拷贝是对于一个对象的顶层拷贝

通俗的理解是:拷贝了引用,并没有拷贝内容

1.2.2深拷贝

copy.deepcopy

1.2.3拷贝的其他方式

浅拷贝对不可变类型和可变类型的copy不同

In [88]: a = [11,22,33]

In [89]: b = copy.copy(a)

In [90]: id(a)

Out[90]:59275144

In [91]: id(b)

Out[91]:59525600

In [92]: a.append(44)

In [93]: a

Out[93]: [11,22,33,44]

In [94]: b

Out[94]: [11,22,33]

In [95]:

In [95]:

In [95]: a = (11,22,33)

In [96]: b = copy.copy(a)

In [97]: id(a)

Out[97]:58890680

In [98]: id(b)

Out[98]:58890680

·分片表达式可以赋值一个序列

a ="abc"

b = a[:]

·字典的copy方法可以拷贝一个字典

d = dict(name="zhangsan", age=27)

co = d.copy()

·有些内置函数可以生成拷贝(list)

a = list(range(10))

b = list(a)

·copy模块中的copy函数

importcopy

a = (1,2,3)

b = copy.copy(a)

1.3属性property

面试题:

1、你对面向对象的理解

2、面向对象的特征是什么

3、对封装的理解?

封装,类本身就是一个封装,封装了属性和方法。方法也是封装,对一些业务逻辑的封装。私有也是封装,将一些方法和属性私有化,对外提供可访问的接口。

4、对继承的理解

将共性的内容放在父类中,子类只需要关注自己特有的内容,共性的继承过来就行了。

这样简化开发,符合逻辑习惯,利于扩展。

5、多态的理解

多态,一个对象在不同的情况下显示不同的形态。在python中因为是弱类型语言,对类型没有限定,所有python中不完全支持多态,但是多态的思想呢,python也是能体现的。

1.3.1私有属性添加getter和setter方法

classMoney(object):

def__init__(self):

self.__money =0

defgetMoney(self):

returnself.__money

defsetMoney(self, value):

ifisinstance(value, int):

self.__money = value

else:

print("error:不是整型数字")

1.3.2使用property升级getter和setter方法

classMoney(object):

def__init__(self):

self.__money =0

defgetMoney(self):

returnself.__money

defsetMoney(self, value):

ifisinstance(value, int):

self.__money = value

else:

print("error:不是整型数字")

money = property(getMoney, setMoney)

运行结果:

In [1]:fromget_setimportMoney

In [2]:

In [2]: a = Money()

In [3]:

In [3]: a.money

Out[3]:0

In [4]: a.money =100

In [5]: a.money

Out[5]:100

In [6]: a.getMoney()

Out[6]:100

1.3.3使用property取代getter和setter方法

@property成为属性函数,可以对属性赋值时做必要的检查,并保证代码的清晰短小,主要有2个作用

·将方法转换为只读

·重新实现一个属性的设置和读取方法,可做边界判定

classMoney(object):

def__init__(self):

self.__money =0

@property

defmoney(self):

returnself.__money

@money.setter

defmoney(self, value):

ifisinstance(value, int):

self.__money = value

else:

print("error:不是整型数字")

运行结果

In [3]: a = Money()

In [4]:

In [4]:

In [4]: a.money

Out[4]:0

In [5]: a.money =100

In [6]: a.money

Out[6]:100

1.4生成器

1.4.1什么是生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

1.4.2创建生成器方法1

要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的[ ]改成( )

In [15]: L = [ x*2forxinrange(5)]

In [16]: L

Out[16]: [0,2,4,6,8]

In [17]: G = ( x*2forxinrange(5))

In [18]: G

Out[18]: at0x7f626c132db0>

In [19]:

创建L和G的区别仅在于最外层的[ ]和( ),L是一个列表,而G是一个生成器。我们可以直接打印出L的每一个元素,但我们怎么打印出G的每一个元素呢?如果要一个一个打印出来,可以通过next()函数获得生成器的下一个返回值:

In [19]: next(G)

Out[19]: 0

In [20]: next(G)

Out[20]: 2

In [21]: next(G)

Out[21]: 4

In [22]: next(G)

Out[22]: 6

In [23]: next(G)

Out[23]: 8

In [24]: next(G)

---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

in ()

----> 1 next(G)

StopIteration:

In [25]:

In [26]: G = ( x*2forxinrange(5))

In [27]:forxinG:

....:     print(x)

....:

0

2

4

6

8

In [28]:

生成器保存的是算法,每次调用next(G),就计算出G的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的异常。当然,这种不断调用next()实在是太变态了,正确的方法是使用for循环,因为生成器也是可迭代对象。所以,我们创建了一个生成器后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration异常。

1.4.3创建生成器方法2

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

In [28]:deffib(times):

....:     n =0

....:     a,b =0,1

....:whilen

....:         print(b)

....:         a,b = b,a+b

....:         n+=1

....:return'done'

....:

In [29]: fib(5)

1

1

2

3

5

Out[29]:'done'

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

In [30]: def fib(times):

....:     n = 0

....:     a,b = 0,1

....:     while n

....:         yield b

....:         a,b = b,a+b

....:         n+=1

....:     return 'done'

....:

In [31]: F = fib(5)

In [32]: next(F)

Out[32]: 1

In [33]: next(F)

Out[33]: 1

In [34]: next(F)

Out[34]: 2

In [35]: next(F)

Out[35]: 3

In [36]: next(F)

Out[36]: 5

In [37]: next(F)

---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

in ()

----> 1 next(F)

StopIteration: done

在上面fib的例子,我们在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:

In [38]:forninfib(5):

....:     print(n)

....:

1

1

2

3

5

In [39]:

但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:

In [39]: g = fib(5)

In [40]:whileTrue:

....:try:

....:         x = next(g)

....:         print("value:%d"%x)

....:exceptStopIterationase:

....:         print("生成器返回值:%s"%e.value)

....:break

....:

value:1

value:1

value:2

value:3

value:5

生成器返回值:done

In [41]:

1.4.4send

例子:执行到yield时,gen函数作用暂时保存,返回i的值;temp接收下次c.send("python"),send发送过来的值,c.next()等价c.send(None)

In [10]:defgen():

....:     i =0

....:whilei<5:

....:         temp =yieldi

....:         print(temp)

....:         i+=1

....:

使用next函数

In [11]: f = gen()

In [12]: next(f)

Out[12]: 0

In [13]: next(f)

None

Out[13]: 1

In [14]: next(f)

None

Out[14]: 2

In [15]: next(f)

None

Out[15]: 3

In [16]: next(f)

None

Out[16]: 4

In [17]: next(f)

None

---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

in ()

----> 1 next(f)

StopIteration:

使用__next__()方法

In [18]: f = gen()

In [19]: f.__next__()

Out[19]: 0

In [20]: f.__next__()

None

Out[20]: 1

In [21]: f.__next__()

None

Out[21]: 2

In [22]: f.__next__()

None

Out[22]: 3

In [23]: f.__next__()

None

Out[23]: 4

In [24]: f.__next__()

None

---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

in ()

----> 1 f.__next__()

StopIteration:

使用send

In [43]: f = gen()

In [44]: f.__next__()

Out[44]:0

In [45]: f.send('haha')

haha

Out[45]:1

In [46]: f.__next__()

None

Out[46]:2

In [47]: f.send('haha')

haha

Out[47]:3

In [48]:

1.4.5实现多任务

模拟多任务实现方式之一:协程

def test1():

while True:

print("--1--")

yield None

def test2():

while True:

print("--2--")

yield None

t1 = test1()

t2 = test2()

while True:

t1.__next__()

t2.__next__()

总结

生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第n次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。

生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。

生成器的特点:

1.节约内存

2.迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的

1.5迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

1.5.1可迭代对象

以直接作用于for循环的数据类型有以下几种:

一类是集合数据类型,如list、tuple、dict、set、str等;

一类是generator,包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。

1.5.2判断是否可以迭代

可以使用isinstance()判断一个对象是否是Iterable对象:

In [50]:fromcollectionsimportIterable

In [51]: isinstance([], Iterable)

Out[51]:True

In [52]: isinstance({}, Iterable)

Out[52]:True

In [53]: isinstance('abc', Iterable)

Out[53]:True

In [54]: isinstance((xforxinrange(10)), Iterable)

Out[54]:True

In [55]: isinstance(100, Iterable)

Out[55]:False

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

1.5.3迭代器

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

可以使用isinstance()判断一个对象是否是Iterator对象:

In [56]:fromcollectionsimportIterator

In [57]: isinstance((xforxinrange(10)), Iterator)

Out[57]:True

In [58]: isinstance([], Iterator)

Out[58]:False

In [59]: isinstance({}, Iterator)

Out[59]:False

In [60]: isinstance('abc', Iterator)

Out[60]:False

In [61]: isinstance(100, Iterator)

Out[61]:False

1.5.4iter()函数

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

把list、dict、str等Iterable变成Iterator可以使用iter()函数:

In [62]: isinstance(iter([]), Iterator)

Out[62]:True

In [63]: isinstance(iter('abc'), Iterator)

Out[63]:True

总结

·凡是可作用于for循环的对象都是Iterable类型;

·凡是可作用于next()函数的对象都是Iterator类型

·集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

·目的是在使用集合的时候,减少占用的内存。

1.6闭包

1.61.1.1什么是闭包.1函数引用

deftest1():

print("--- in test1 func----")

#调用函数

test1()

#引用函数

ret = test1

print(id(ret))

print(id(test1))

#通过引用调用函数

ret()

运行结果:

---intest1 func----

140212571149040

140212571149040

---intest1 func----

1.6.2 什么是闭包

定义一个函数

deftest(number):

#在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包

deftest_in(number_in):

print("in test_in函数, number_in is %d"%number_in)

returnnumber+number_in

#其实这里返回的就是闭包的结果

returntest_in

#给test函数赋值,这个20就是给参数number

ret = test(20)

#注意这里的100其实给参数number_in

print(ret(100))

#注意这里的200其实给参数number_in

print(ret(200))

运行结果:

intest_in函数, number_inis100

120

intest_in函数, number_inis200

220

1.6.3看一个闭包的实际例子:

defline_conf(a, b):

defline(x):

returna*x + b

returnline

line1 = line_conf(1,1)

line2 = line_conf(4,5)

print(line1(5))

print(line2(5))

这个例子中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。

如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。

闭包思考:

1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成

2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

1.7.装饰器

装饰器是程序开发中经常会用到的一个功能,用好了装饰器,开发效率如虎添翼,所以这也是Python面试中必问的问题,但对于好多初次接触这个知识的人来讲,这个功能有点绕,自学时直接绕过去了,然后面试问到了就挂了,因为装饰器是程序开发的基础知识,这个都不会,别跟人家说你会Python,看了下面的文章,保证你学会装饰器。

1.7.1装饰器理解

1、先明白这段代码

####第一波####

deffoo():

print('foo')

foo#表示是函数

foo()#表示执行foo函数

####第二波####

deffoo():

print('foo')

foo =lambdax: x +1

foo()#执行下面的lambda表达式,而不再是原来的foo函数,因为foo这个名字被重新指向了另外一个匿名函数

2、需求来了

初创公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需调用基础平台提供的功能即可。如下:

###############基础平台提供的功能如下###############

deff1():

print('f1')

deff2():

print('f2')

deff3():

print('f3')

deff4():

print('f4')

###############业务部门A调用基础平台提供的功能###############

f1()

f2()

f3()

f4()

###############业务部门B调用基础平台提供的功能###############

f1()

f2()

f3()

f4()

目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写代码时候没有关注验证相关的问题,即:基础平台的提供的功能可以被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。

老大把工作交给Low B,他是这么做的:

跟每个业务部门交涉,每个业务部门自己写代码,调用基础平台的功能之前先验证。诶,这样一来基础平台就不需要做任何修改了。太棒了,有充足的时间泡妹子...

当天Low B被开除了…

老大把工作交给Low BB,他是这么做的:

###############基础平台提供的功能如下###############

deff1():

#验证1

#验证2

#验证3

print('f1')

deff2():

#验证1

#验证2

#验证3

print('f2')

deff3():

#验证1

#验证2

#验证3

print('f3')

deff4():

#验证1

#验证2

#验证3

print('f4')

###############业务部门不变###############

###业务部门A调用基础平台提供的功能###

f1()

f2()

f3()

f4()

###业务部门B调用基础平台提供的功能###

f1()

f2()

f3()

f4()

过了一周Low BB被开除了…

老大把工作交给Low BBB,他是这么做的:

只对基础平台的代码进行重构,其他业务部门无需做任何修改

###############基础平台提供的功能如下###############

defcheck_login():

#验证1

#验证2

#验证3

pass

deff1():

check_login()

print('f1')

deff2():

check_login()

print('f2')

deff3():

check_login()

print('f3')

deff4():

check_login()

print('f4')

老大看了下Low BBB的实现,嘴角漏出了一丝的欣慰的笑,语重心长的跟Low BBB聊了个天:

老大说:

写代码要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

·封闭:已实现的功能代码块

·开放:对扩展开发

如果将开放封闭原则应用在上述需求中,那么就不允许在函数f1、f2、f3、f4的内部进行修改代码,老板就给了Low BBB一个实现方案:

defw1(func):

definner():

#验证1

#验证2

#验证3

func()

returninner

@w1

deff1():

print('f1')

@w1

deff2():

print('f2')

@w1

deff3():

print('f3')

@w1

deff4():

print('f4')

对于上述代码,也是仅仅对基础平台的代码进行修改,就可以实现在其他人调用函数f1 f2 f3 f4之前都进行【验证】操作,并且其他业务部门无需做任何操作。

Low BBB心惊胆战的问了下,这段代码的内部执行原理是什么呢?

老大正要生气,突然Low BBB的手机掉到地上,恰巧屏保就是Low BBB的女友照片,老大一看一紧一抖,喜笑颜开,决定和Low BBB交个好朋友。

详细的开始讲解了:

单独以f1为例:

defw1(func):

definner():

#验证1

#验证2

#验证3

func()

returninner

@w1

deff1():

print('f1')

python解释器就会从上到下解释代码,步骤如下:

1.def w1(func): ==>将w1函数加载到内存

2.@w1

没错,从表面上看解释器仅仅会解释这两句代码,因为函数在没有被调用之前其内部代码不会被执行。

从表面上看解释器着实会执行这两句,但是@w1这一句代码里却有大文章,@函数名 是python的一种语法糖。

上例@w1内部会执行一下操作:

执行w1函数

执行w1函数 ,并将@w1下面的函数作为w1函数的参数,即:@w1等价于w1(f1)所以,内部就会去执行:

definner():

#验证1

#验证2

#验证3

f1()# func是参数,此时func等于f1

returninner#返回的inner,inner代表的是函数,非执行函数,其实就是将原来的f1函数塞进另外一个函数中

w1的返回值

将执行完的w1函数返回值 赋值 给@w1下面的函数的函数名f1即将w1的返回值再重新赋值给f1,即:

新f1 =definner():

#验证1

#验证2

#验证3

原来f1()

returninner

所以,以后业务部门想要执行f1函数时,就会执行 新f1函数,在新f1函数内部先执行验证,再执行原来的f1函数,然后将原来f1函数的返回值返回给了业务调用者。

如此一来,即执行了验证的功能,又执行了原来f1函数的内容,并将原f1函数返回值 返回给业务调用着

Low BBB你明白了吗?要是没明白的话,我晚上去你家帮你解决吧!!!

1.1.2多个装饰器

#定义函数:完成包裹数据

defmakeBold(fn):

defwrapped():

return""+ fn() +""

returnwrapped

#定义函数:完成包裹数据

defmakeItalic(fn):

defwrapped():

return""+ fn() +""

returnwrapped

@makeBold

deftest1():

return"hello world-1"

@makeItalic

deftest2():

return"hello world-2"

@makeBold

@makeItalic

deftest3():

return"hello world-3"

print(test1()))

print(test2()))

print(test3()))

运行结果:

hello world-1

hello world-2

hello world-3

1.7.3装饰器(decorator)功能

1.引入日志

2.函数执行时间统计

3.执行函数前预备处理

4.执行函数后清理功能

5.权限校验等场景

6.缓存

1.7.4装饰器示例

1.7.4.1例1:无参数的函数

fromtimeimportctime, sleep

deftimefun(func):

defwrappedfunc():

print("%s called at %s"%(func.__name__, ctime()))

func()

returnwrappedfunc

@timefun

deffoo():

print("I am foo")

foo()

sleep(2)

foo()

上面代码理解装饰器执行行为可理解成

foo = timefun(foo)

#foo先作为参数赋值给func后,foo接收指向timefun返回的wrappedfunc

foo()

#调用foo(),即等价调用wrappedfunc()

#内部函数wrappedfunc被引用,所以外部函数的func变量(自由变量)并没有释放

#func里保存的是原foo函数对象

1.7.2例2:被装饰的函数有参数

fromtimeimportctime, sleep

deftimefun(func):

defwrappedfunc(a, b):

print("%s called at %s"%(func.__name__, ctime()))

print(a, b)

func(a, b)

returnwrappedfunc

@timefun

deffoo(a, b):

print(a+b)

foo(3,5)

sleep(2)

foo(2,4)

1.7.3例3:被装饰的函数有不定长参数

fromtimeimportctime, sleep

deftimefun(func):

defwrappedfunc(*args, **kwargs):

print("%s called at %s"%(func.__name__, ctime()))

func(*args, **kwargs)

returnwrappedfunc

@timefun

deffoo(a, b, c):

print(a+b+c)

foo(3,5,7)

sleep(2)

foo(2,4,9)

1.7.4例4:装饰器中的return

fromtimeimportctime, sleep

deftimefun(func):

defwrappedfunc():

print("%s called at %s"%(func.__name__, ctime()))

func()

returnwrappedfunc

@timefun

deffoo():

print("I am foo")

@timefun

defgetInfo():

return'----hahah---'

foo()

sleep(2)

foo()

print(getInfo())

执行结果:

foo called at Fri Nov421:55:352016

I am foo

foo called at Fri Nov421:55:372016

I am foo

getInfo called at Fri Nov421:55:372016

None

如果修改装饰器为return func(),则运行结果:

foo called at Fri Nov421:55:572016

I am foo

foo called at Fri Nov421:55:592016

I am foo

getInfo called at Fri Nov421:55:592016

----hahah---

总结:

·一般情况下为了让装饰器更通用,可以有return

1.7.5例5:装饰器带参数,在原有装饰器的基础上,设置外部变量

#decorator2.py

fromtimeimportctime, sleep

deftimefun_arg(pre="hello"):

deftimefun(func):

defwrappedfunc():

print("%s called at %s %s"%(func.__name__, ctime(), pre))

returnfunc()

returnwrappedfunc

returntimefun

@timefun_arg("wangcai")

deffoo():

print("I am foo")

@timefun_arg("python")

deftoo():

print("I am too")

foo()

sleep(2)

foo()

too()

sleep(2)

too()

可以理解为

foo()==timefun_arg("wangcai")(foo)()

1.2python是动态语言

1.2.1动态语言的定义

动态编程语言是高级程序设计语言的一个类别,在计算机科学领域已被广泛应用。它是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。动态语言目前非常具有活力。例如JavaScript便是一个动态语言,除此之外如PHP、Ruby、Python等也都属于动态语言,而C、C++等语言则不属于动态语言。----来自 维基百科

1.2.2运行的过程中给对象绑定(添加)属性

>>>classPerson(object):

def__init__(self, name = None, age = None):

self.name = name

self.age = age

>>>P = Person("小明","24")

>>>

在这里,我们定义了1个类Person,在这个类里,定义了两个初始属性name和age,但是人还有性别啊!如果这个类不是你写的是不是你会尝试访问性别这个属性呢?

>>>P.sex ="male"

>>>P.sex

'male'

>>>

这时候就发现问题了,我们定义的类里面没有sex这个属性啊!怎么回事呢? 这就是动态语言的魅力和坑! 这里 实际上就是 动态给实例绑定属性!

1.2.3运行的过程中给类绑定(添加)属性

>>>P1 = Person("小丽","25")

>>>P1.sex

Traceback (most recent call last):

File"", line1,in

P1.sex

AttributeError: Person instance has no attribute'sex'

>>>

我们尝试打印P1.sex,发现报错,P1没有sex这个属性!----给P这个实例绑定属性对P1这个实例不起作用! 那我们要给所有的Person的实例加上sex属性怎么办呢? 答案就是直接给Person绑定属性!

>>>> Person.sex =None#给类Person添加一个属性

>>>P1 = Person("小丽","25")

>>>print(P1.sex)#如果P1这个实例对象中没有sex属性的话,那么就会访问它的类属性

None#可以看到没有出现异常

>>>

1.2.4运行的过程中给类绑定(添加)方法

我们直接给Person绑定sex这个属性,重新实例化P1后,P1就有sex这个属性了! 那么function呢?怎么绑定?

>>>classPerson(object):

def__init__(self, name = None, age = None):

self.name = name

self.age = age

defeat(self):

print("eat food")

>>>defrun(self, speed):

print("%s在移动,速度是%d km/h"%(self.name, speed))

>>>P = Person("老王",24)

>>>P.eat()

eat food

>>>

>>>P.run()

Traceback (most recent call last):

File"", line1,in

P.run()

AttributeError: Person instance has no attribute'run'

>>>

>>>

>>>importtypes

>>>P.run = types.MethodType(run, P)

>>>P.run(180)

老王在移动,速度是180km/h

既然给类添加方法,是使用类名.方法名= xxxx,那么给对象添加一个方法也是类似的对象.方法名= xxxx

完整的代码如下:

importtypes

#定义了一个类

classPerson(object):

num =0

def__init__(self, name = None, age = None):

self.name = name

self.age = age

defeat(self):

print("eat food")

#定义一个实例方法

defrun(self, speed):

print("%s在移动,速度是%d km/h"%(self.name, speed))

#定义一个类方法

@classmethod

deftestClass(cls):

cls.num =100

#定义一个静态方法

@staticmethod

deftestStatic():

print("---static method----")

#创建一个实例对象

P = Person("老王",24)

#调用在class中的方法

P.eat()

#给这个对象添加实例方法

P.run = types.MethodType(run, P)

#调用实例方法

P.run(180)

#给Person类绑定类方法

Person.testClass = testClass

#调用类方法

print(Person.num)

Person.testClass()

print(Person.num)

#给Person类绑定静态方法

Person.testStatic = testStatic

#调用静态方法

Person.testStatic()

1.2.5运行的过程中删除属性、方法

删除的方法:

1.del对象.属性名

2.delattr(对象, "属性名")

通过以上例子可以得出一个结论:相对于动态语言,静态语言具有严谨性!所以,玩动态语言的时候,小心动态的坑!

那么怎么避免这种情况呢?请使用__slots__,

1.2.6__slots__

现在我们终于明白了,动态语言与静态语言的不同

动态语言:可以在运行的过程中,修改代码

静态语言:编译时已经确定好代码,运行过程中不能修改

如果我们想要限制实例的属性怎么办?比如,只允许对Person实例添加name和age属性。

为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:

>>>classPerson(object):

__slots__ = ("name","age")

>>>P = Person()

>>>P.name ="老王"

>>>P.age =20

>>>P.score =100

Traceback (most recent call last):

File"", line1,in

AttributeError: Person instance has no attribute'score'

>>>

1.2.6.1注意:

·使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

In [67]:classTest(Person):

...:pass

...:

In [68]: t = Test()

In [69]: t.score =100

你可能感兴趣的:(python 高级编程)