1、*args和**kwargs是什么意思?
答:args表示可變參數(shù)(variadic arguments),它允許你傳入0個(gè)或任意個(gè)無(wú)名參數(shù),這些參數(shù)在函數(shù)調(diào)用時(shí)自動(dòng)組裝為一個(gè)tuple; kwargs表示關(guān)鍵字參數(shù)(keyword arguments),它允許你傳入0個(gè)或任意個(gè)含參數(shù)名的參數(shù),這些關(guān)鍵字參數(shù)在函數(shù)內(nèi)部自動(dòng)組裝為一個(gè)dict。同時(shí)使用args和kwargs的時(shí)候,必須保證args在*kwargs之前。


2、python里面如何拷貝一個(gè)對(duì)象?
答:
(1) 賦值(=),就是創(chuàng)建了對(duì)象的一個(gè)新的引用,修改其中任意一個(gè)變量都會(huì)影響到另一個(gè);
(2)淺拷貝(copy.copy()),創(chuàng)建一個(gè)新的對(duì)象,但它包含的是對(duì)原始對(duì)象中包含項(xiàng)的引用(如果用引用的方式修改其中一個(gè)對(duì)象,另一個(gè)也會(huì)被改變);
(3)深拷貝(copy.deepcopy()),創(chuàng)建一個(gè)新的對(duì)象,并且遞歸的復(fù)制它所包含的對(duì)象(修改其中一個(gè),另一個(gè)不會(huì)改變)
注意:并不是所有的對(duì)象都可以拷貝
引用
In [1]: will = ['will',28,['a','c','java']]
In [2]: wilber = will
In [3]: id(will)
Out[3]: 4398736904
In [4]: [id(ele) for ele in will]
Out[4]: [4397922096, 4305293280, 4398736968]
In [5]: id(wilber)
Out[5]: 4398736904
In [6]: [id(ele) for ele in wilber]
Out[6]: [4397922096, 4305293280, 4398736968]
In [7]: will[0] = 'wilber'
In [8]: will[2].append('css')
In [9]: id(will)
Out[9]: 4398736904
In [10]: will
Out[10]: ['wilber', 28, ['a', 'c', 'java', 'css']]
In [11]: wilber
Out[11]: ['wilber', 28, ['a', 'c', 'java', 'css']]
In [12]: id(wilber)
Out[12]: 4398736904
In [13]: [id(ele) for ele in will]
Out[13]: [4398651968, 4305293280, 4398736968]
In [14]: [id(ele) for ele in wilber]
Out[14]: [4398651968, 4305293280, 4398736968]
注意里面對(duì)will[0]的修改,地址是發(fā)生了變化的
淺拷貝
In [20]: import copy
In [21]: will = ["Will", 28, ["Python", "C#", "JavaScript"]]
In [22]: wilber = copy.copy(will)
In [23]: id(will)
Out[23]: 4400270152
In [24]: id(wilber)
Out[24]: 4400681928
In [25]: [id(ele) for ele in will]
Out[25]: [4400208672, 4305293280, 4400666120]
In [26]: [id(ele) for ele in wilber]
Out[26]: [4400208672, 4305293280, 4400666120]
In [27]: will[0] = "hello"
In [28]: will[2].append("css")
In [29]: will
Out[29]: ['hello', 28, ['Python', 'C#', 'JavaScript', 'css']]
In [30]: wilber
Out[30]: ['Will', 28, ['Python', 'C#', 'JavaScript', 'css']]
In [31]: id(will)
Out[31]: 4400270152
In [32]: id(wilber)
Out[32]: 4400681928
In [33]: [id(ele) for ele in will]
Out[33]: [4400262928, 4305293280, 4400666120]
In [34]: [id(ele) for ele in wilber]
Out[34]: [4400208672, 4305293280, 4400666120]
1、首先,依然使用一個(gè)will變量,指向一個(gè)list類(lèi)型的對(duì)象
2、然后,通過(guò)copy模塊里面的淺拷貝函數(shù)copy(),對(duì)will指向的對(duì)象進(jìn)行淺拷貝,然后淺拷貝生成的新對(duì)象賦值給wilber變量
-淺拷貝會(huì)創(chuàng)建一個(gè)新的對(duì)象,這個(gè)例子中"wilber is not will"
-但是,對(duì)于對(duì)象中的元素,淺拷貝就只會(huì)使用原始元素的引用(內(nèi)存地址),也就是說(shuō)"wilber[i] is will[i]"
3、當(dāng)對(duì)will進(jìn)行修改的時(shí)候
-由于list的第一個(gè)元素是不可變類(lèi)型,所以will對(duì)應(yīng)的list的第一個(gè)元素會(huì)使用一個(gè)新的對(duì)象
-但是list的第三個(gè)元素是一個(gè)可不類(lèi)型,修改操作不會(huì)產(chǎn)生新的對(duì)象,所以will的修改結(jié)果會(huì)相應(yīng)的反應(yīng)到wilber上
In [35]: wilber[0] = "help"
In [36]: will
Out[36]: ['hello', 28, ['Python', 'C#', 'JavaScript', 'css']]
In [37]: wilber
Out[37]: ['help', 28, ['Python', 'C#', 'JavaScript', 'css']]
In [38]: [id(ele) for ele in will]
Out[38]: [4400262928, 4305293280, 4400666120]
In [39]: [id(ele) for ele in wilber]
Out[39]: [4326014848, 4305293280, 4400666120]
我們使用切片的時(shí)候會(huì)發(fā)生淺拷貝
深拷貝
In [60]: will = ["Will", 28, ["Python", "C#", "JavaScript"]]
In [61]: wilber = copy.deepcopy(will)
In [62]: id(will)
Out[62]: 4397900104
In [63]: id(wilber)
Out[63]: 4399747976
In [64]: [id(ele) for ele in will]
Out[64]: [4400848320, 4305293280, 4397933576]
In [65]: [id(ele) for ele in wilber]
Out[65]: [4400848320, 4305293280, 4397830984]
In [66]: will[0] = "Wilber"
In [67]: will[2].append("CSS")
In [68]: id(will)
Out[68]: 4397900104
In [69]: id(wilber)
Out[69]: 4399747976
In [70]: [id(ele) for ele in will]
Out[70]: [4400846360, 4305293280, 4397933576]
In [71]: [id(ele) for ele in wilber]
Out[71]: [4400848320, 4305293280, 4397830984]
In [72]: will
Out[72]: ['Wilber', 28, ['Python', 'C#', 'JavaScript', 'CSS']]
In [73]: wilber
Out[73]: ['Will', 28, ['Python', 'C#', 'JavaScript']]
1、首先,同樣使用一個(gè)will變量,指向一個(gè)list類(lèi)型的對(duì)象
2、然后,通過(guò)copy模塊里面的深拷貝函數(shù)deepcopy(),對(duì)will指向的對(duì)象進(jìn)行深拷貝,然后深拷貝生成的新對(duì)象賦值給wilber變量
-跟淺拷貝類(lèi)似,深拷貝也會(huì)創(chuàng)建一個(gè)新的對(duì)象,這個(gè)例子中"wilber is not will"
-但是,對(duì)于對(duì)象中的元素,深拷貝都會(huì)重新生成一份(有特殊情況,下面會(huì)說(shuō)明),而不是簡(jiǎn)單的使用原始元素的引用(內(nèi)存地址)
--例子中will的第三個(gè)元素指向39737304,而wilber的第三個(gè)元素是一個(gè)全新的對(duì)象39773088,也就是說(shuō),"wilber[2] is not will[2]"
3、當(dāng)對(duì)will進(jìn)行修改的時(shí)候
-由于list的第一個(gè)元素是不可變類(lèi)型,所以will對(duì)應(yīng)的list的第一個(gè)元素會(huì)使用一個(gè)新的對(duì)象39758496
-但是list的第三個(gè)元素是一個(gè)可不類(lèi)型,修改操作不會(huì)產(chǎn)生新的對(duì)象,但是由于"wilber[2] is not will[2]",所以will的修改不會(huì)影響wilber
參考:http://www.cnblogs.com/wilber2013/p/4645353.html
3、__new__和__init__的區(qū)別
很多同學(xué)都以為Python中的init是構(gòu)造方法,但其實(shí)不然,Python中真正的構(gòu)造方法是new。init和new有什么區(qū)別?本文就來(lái)探討一下。
我們先來(lái)看一下init的用法
class Person(object):
def __init__(self, name, age):
print("in __init__")
self._name = name
self._age = age
p = Person("Wang", 33)
上面的代碼會(huì)輸出如下的結(jié)果
in__init__
<__main__.Person object at 0x7fb2e0936450>
那么我們思考一個(gè)問(wèn)題,Python中要實(shí)現(xiàn)Singleton怎么實(shí)現(xiàn),要實(shí)現(xiàn)工廠模式怎么實(shí)現(xiàn)?
用init函數(shù)似乎沒(méi)法做到呢~
實(shí)際上,init函數(shù)并不是真正意義上的構(gòu)造函數(shù),init方法做的事情是在對(duì)象創(chuàng)建好之后初始化變量。真正創(chuàng)建實(shí)例的是new方法。
我們來(lái)看下面的例子
class Person(object):
def __new__(cls, *args, **kwargs):
print("in __new__")
instance = object.__new__(cls, *args, **kwargs)
return instance
def __init__(self, name, age):
print("in __init__")
self._name = name
self._age = age
p = Person("Wang", 33)
上面的代碼輸出如下的結(jié)果
in __new__
in __init__
上面的代碼中實(shí)例化了一個(gè)Person對(duì)象,可以看到new和init都被調(diào)用了。new方法用于創(chuàng)建對(duì)象并返回對(duì)象,當(dāng)返回對(duì)象時(shí)會(huì)自動(dòng)調(diào)用init方法進(jìn)行初始化。new方法是靜態(tài)方法,而init是實(shí)例方法。
好了,理解new和init的區(qū)別后,我們?cè)賮?lái)看一下前面提出的問(wèn)題,用Python怎么實(shí)現(xiàn)Singleton,怎么實(shí)現(xiàn)工廠模式?
先來(lái)看Singleton
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = object.__new__(cls, *args, **kwargs)
return cls._instance
s1 = Singleton()
s2 = Singleton()
print(s1)
print(s2)
上面的代碼輸出
<__main__.Singleton object at 0x7fdef58b1190>
<__main__.Singleton object at 0x7fdef58b1190>
可以看到s1和s2都指向同一個(gè)對(duì)象,實(shí)現(xiàn)了單例模式。
再來(lái)看下工廠模式的實(shí)現(xiàn)
class Fruit(object):
def __init__(self):
pass
def print_color(self):
pass
class Apple(Fruit):
def __init__(self):
pass
def print_color(self):
print("apple is in red")
class Orange(Fruit):
def __init__(self):
pass
def print_color(self):
print("orange is in orange")
class FruitFactory(object):
fruits = {"apple": Apple, "orange": Orange}
def __new__(cls, name):
if name in cls.fruits.keys():
return cls.fruits[name]()
else:
return Fruit()
fruit1 = FruitFactory("apple")
fruit2 = FruitFactory("orange")
fruit1.print_color()
fruit2.print_color()
上面的代碼輸出
apple is in red
orange is in orange
4、python中的可變對(duì)象和不可變對(duì)象
Python中有可變對(duì)象和不可變對(duì)象之分??勺儗?duì)象創(chuàng)建后可改變但地址不會(huì)改變,即變量指向的還是原來(lái)的變量;不可變對(duì)象創(chuàng)建之后便不能改變,如果改變則會(huì)指向一個(gè)新的對(duì)象。
Python中dict、list是可變對(duì)象,str、int、tuple、float是不可變對(duì)象。
來(lái)看一個(gè)字符串的例子
a = "hello"
print(id(a)) # 輸出 140022851974560
a[0]="a" # 拋出異常:TypeError: 'str' object does not support item assignment
a = a + " world"
print(id(a)) # 輸出 140022850763824
上面的例子里,修改a指向的對(duì)象的值會(huì)導(dǎo)致拋出異常。
執(zhí)行 a = a + " world"時(shí),先計(jì)算等號(hào)右邊的表達(dá)式,生成一個(gè)新的對(duì)象賦值到變量a,因此a指向的對(duì)象發(fā)生了改變,id(a) 的值也與原先不同。
再看一個(gè)列表的例子
a = [1,2,3]
print(id(a)) # 輸出 140022851303976
a[0]=5
print(a) # 輸出 [5, 2, 3]
print(id(a)) # 輸出 140022851303976
a.append(5)
print(a) # 輸出 [5, 2, 3, 5]
print(id(a)) # 輸出 140022851303976
b = a
print(id(b)) # 輸出 140022851303976
b[0] = 6
print(b) # 輸出 [6, 2, 3, 5]
print(a) # 輸出 [6, 2, 3, 5]
c = b[:]
print(id(c)) # 輸出 140022851006760
print(id(b)) # 輸出 140022851303976
c.append(7)
print(c) # 輸出 [6, 2, 3, 5, 7]
print(b) # 輸出 [6, 2, 3, 5]
上面對(duì)a修改元素、添加元素,變量a還是指向原來(lái)的對(duì)象。
將a賦值給b后,變量b和a都指向同一個(gè)對(duì)象,因此修改b的元素值也會(huì)影響a。
變量c是對(duì)b的切片操作的返回值,切片操作相當(dāng)于淺拷貝,會(huì)生成一個(gè)新的對(duì)象,因此c指向的對(duì)象不再是b所指向的對(duì)象,對(duì)c的操作不會(huì)改變b的值。