Python高頻率面試題

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。initnew有什么區(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ì)象,可以看到newinit都被調(diào)用了。new方法用于創(chuàng)建對(duì)象并返回對(duì)象,當(dāng)返回對(duì)象時(shí)會(huì)自動(dòng)調(diào)用init方法進(jìn)行初始化。new方法是靜態(tài)方法,而init是實(shí)例方法。
好了,理解newinit的區(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的值。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • http://python.jobbole.com/85231/ 關(guān)于專業(yè)技能寫(xiě)完項(xiàng)目接著寫(xiě)寫(xiě)一名3年工作經(jīng)驗(yàn)的J...
    燕京博士閱讀 7,794評(píng)論 1 118
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類(lèi)相關(guān)的語(yǔ)法,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 34,644評(píng)論 18 399
  • 今天,看了三集奇葩說(shuō),聽(tīng)了小白羊的收賬分享,我有一次開(kāi)始反思自己,我不停的問(wèn)自己,我活在這個(gè)世界上的意義是什么?我...
    康康_9342閱讀 132評(píng)論 0 0
  • 發(fā)現(xiàn)自己最近真的是越來(lái)越感性了,往往一個(gè)鏡頭就能讓我感動(dòng)良久。有時(shí)候還真是會(huì)為這種是不是是“濫情”苦惱一下,不過(guò)這...
    琴棋詩(shī)花酒閱讀 375評(píng)論 0 0

友情鏈接更多精彩內(nèi)容