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中真正的构造方法是new。init和new有什么区别?本文就来探讨一下。
我们先来看一下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对象,可以看到new和init都被调用了。new方法用于创建对象并返回对象,当返回对象时会自动调用init方法进行初始化。new方法是静态方法,而init是实例方法。
好了,理解new和init的区别后,我们再来看一下前面提出的问题,用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的值。











网友评论