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
�