美文网首页
Python高频率面试题

Python高频率面试题

作者: 3b899188980c | 来源:发表于2018-04-18 10:36 被阅读37次

1、*args和**kwargs是什么意思?

答:args表示可变参数(variadic arguments),它允许你传入0个或任意个无名参数,这些参数在函数调用时自动组装为一个tuple; kwargs表示关键字参数(keyword arguments),它允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。同时使用args和kwargs的时候,必须保证args在*kwargs之前。


2、python里面如何拷贝一个对象?

答:
(1) 赋值(=),就是创建了对象的一个新的引用,修改其中任意一个变量都会影响到另一个;
(2)浅拷贝(copy.copy()),创建一个新的对象,但它包含的是对原始对象中包含项的引用(如果用引用的方式修改其中一个对象,另一个也会被改变);
(3)深拷贝(copy.deepcopy()),创建一个新的对象,并且递归的复制它所包含的对象(修改其中一个,另一个不会改变)

注意:并不是所有的对象都可以拷贝

引用

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]

注意里面对will[0]的修改,地址是发生了变化的

浅拷贝

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、首先,依然使用一个will变量,指向一个list类型的对象
2、然后,通过copy模块里面的浅拷贝函数copy(),对will指向的对象进行浅拷贝,然后浅拷贝生成的新对象赋值给wilber变量
-浅拷贝会创建一个新的对象,这个例子中"wilber is not will"
-但是,对于对象中的元素,浅拷贝就只会使用原始元素的引用(内存地址),也就是说"wilber[i] is will[i]"
3、当对will进行修改的时候
-由于list的第一个元素是不可变类型,所以will对应的list的第一个元素会使用一个新的对象
-但是list的第三个元素是一个可不类型,修改操作不会产生新的对象,所以will的修改结果会相应的反应到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]

我们使用切片的时候会发生浅拷贝

深拷贝

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、首先,同样使用一个will变量,指向一个list类型的对象
2、然后,通过copy模块里面的深拷贝函数deepcopy(),对will指向的对象进行深拷贝,然后深拷贝生成的新对象赋值给wilber变量
-跟浅拷贝类似,深拷贝也会创建一个新的对象,这个例子中"wilber is not will"
-但是,对于对象中的元素,深拷贝都会重新生成一份(有特殊情况,下面会说明),而不是简单的使用原始元素的引用(内存地址)
--例子中will的第三个元素指向39737304,而wilber的第三个元素是一个全新的对象39773088,也就是说,"wilber[2] is not will[2]"
3、当对will进行修改的时候
-由于list的第一个元素是不可变类型,所以will对应的list的第一个元素会使用一个新的对象39758496
-但是list的第三个元素是一个可不类型,修改操作不会产生新的对象,但是由于"wilber[2] is not will[2]",所以will的修改不会影响wilber

参考:http://www.cnblogs.com/wilber2013/p/4645353.html

3、__new__和__init__的区别

很多同学都以为Python中的init是构造方法,但其实不然,Python中真正的构造方法是newinitnew有什么区别?本文就来探讨一下。
我们先来看一下init的用法

class Person(object):
    def __init__(self, name, age):
        print("in __init__")
        self._name = name
        self._age = age 

p = Person("Wang", 33)

上面的代码会输出如下的结果

in__init__
<__main__.Person object at 0x7fb2e0936450>

那么我们思考一个问题,Python中要实现Singleton怎么实现,要实现工厂模式怎么实现?
init函数似乎没法做到呢~
实际上,init函数并不是真正意义上的构造函数,init方法做的事情是在对象创建好之后初始化变量。真正创建实例的是new方法。
我们来看下面的例子

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)

上面的代码输出如下的结果

in __new__
in __init__

上面的代码中实例化了一个Person对象,可以看到newinit都被调用了。new方法用于创建对象并返回对象,当返回对象时会自动调用init方法进行初始化。new方法是静态方法,而init是实例方法。
好了,理解newinit的区别后,我们再来看一下前面提出的问题,用Python怎么实现Singleton,怎么实现工厂模式?
先来看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都指向同一个对象,实现了单例模式。
再来看下工厂模式的实现

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中的可变对象和不可变对象

Python中有可变对象和不可变对象之分。可变对象创建后可改变但地址不会改变,即变量指向的还是原来的变量;不可变对象创建之后便不能改变,如果改变则会指向一个新的对象。
Python中dict、list是可变对象,str、int、tuple、float是不可变对象。
来看一个字符串的例子

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指向的对象的值会导致抛出异常。

执行 a = a + " world"时,先计算等号右边的表达式,生成一个新的对象赋值到变量a,因此a指向的对象发生了改变,id(a) 的值也与原先不同。

再看一个列表的例子

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]

上面对a修改元素、添加元素,变量a还是指向原来的对象。

将a赋值给b后,变量b和a都指向同一个对象,因此修改b的元素值也会影响a。
变量c是对b的切片操作的返回值,切片操作相当于浅拷贝,会生成一个新的对象,因此c指向的对象不再是b所指向的对象,对c的操作不会改变b的值。

相关文章

网友评论

      本文标题:Python高频率面试题

      本文链接:https://www.haomeiwen.com/subject/zlqvkftx.html