面试题
- python函数参数传递
- @staticmethod和@classmethod
- 类变量和实例变量
- Python自省
- 字典推导式
- Python中单下划线和双下划线
- 字符串格式化:%和.format
- 迭代器和生成器
- *args and **kwargs
- Python垃圾回收机制
- python中is和==
- python中的拷贝
- python中open和with open
python函数参数传递
函数参数由实参传递给形参的过程,是由参数传递机制控制的。根据实际参数的类型不同,函数参数的传递方式分为值传递和引用传递(地址传递)。
值传递:实际上是将实际参数值的副本(复制品)传入函数,而参数本省不会受到影响。因此,在函数内部的操作,不会影响实际参数值。
def swap(a , b) :
# 下面代码实现a、b变量的值交换
a, b = b, a
print("swap函数里,a的值是", \
a, ";b的值是", b)
a = 6
b = 9
swap(a , b)
print("交换结束后,变量a的值是", \
a , ";变量b的值是", b)
# 结果
#swap函数里,a的值是 9 ;b的值是 6
#交换结束后,变量a的值是 6 ;变量b的值是 9
引用传递(地址传递):当实际参数的数据类是可变对象时,函数参数的传递方式为引用传递。在函数内部的操作也会影响实际参数。
def swap(dw):
# 下面代码实现dw的a、b两个元素的值交换
dw['a'], dw['b'] = dw['b'], dw['a']
print("swap函数里,a元素的值是",\
dw['a'], ";b元素的值是", dw['b'])
dw = {'a': 6, 'b': 9}
swap(dw)
print("交换结束后,a元素的值是",\
dw['a'], ";b元素的值是", dw['b'])
# 结果
swap函数里,a元素的值是 9 ;b元素的值是 6
交换结束后,a元素的值是 9 ;b元素的值是 6
@staticmethod和@classmethod
@staticmethod:静态方法,@classmethod:类方法,另外还有 实例方法。
def foo(x):
print "executing foo(%s)"%(x)
class A(object):
def foo(self,x):
print "executing foo(%s,%s)"%(self,x)
@classmethod
def class_foo(cls,x):
print "executing class_foo(%s,%s)"%(cls,x)
@staticmethod
def static_foo(x):
print "executing static_foo(%s)"%x
a=A()
类定义中 self或cls是对实例或类的绑定。对于普通函数,直接调用foo(x);但对于实例方法,在类中定义方法时,需要绑定这个实例,即foo(self,x),那么调用时是a.foo(x)(其实是foo(a,x));类方法一样,但它传递的是类,不需要实例化类就可以被类本身调用;对于静态方法,不需要对谁进行绑定,但调用时仍需要a.static_foo(x)来调用.
类变量和实例变量
类变量:是可在类的所有实例之间共享的变量。
实例变量:实例化后,每个实例单独拥有的变量
class Test(object):
num_of_instance = 0
def __init__(self, name):
self.name = name # 实例变量
Test.num_of_instance += 1 # 类变量
if __name__ == '__main__':
print Test.num_of_instance # 0
t1 = Test('jack')
print Test.num_of_instance # 1
t2 = Test('lucy')
print t1.name , t1.num_of_instance # jack 2
print t2.name , t2.num_of_instance # lucy 2
class Person:
name="aaa"
p1=Person()
p2=Person()
p1.name="bbb"
print p1.name # bbb
print p2.name # aaa
print Person.name # aaa
class Person:
name=[]
p1=Person()
p2=Person()
p1.name.append(1)
print p1.name # [1]
print p2.name # [1]
print Person.name # [1]
定义类时,name同样是指类变量,但p1.name="bbb"是实例调用类变量并进行赋值操作,在p1的作用域中把类变量的引用改变了,就变为一个实例变量。
而p1.name.append(1)对应的类变量是可变变量,在进行操作时,引用传递,会对类变量产生影响。
Python自省
自省是指 面向对象的语言所写的程序在运行时,就能知道对象的特性,即运行时能够获得对象的类型。如
type() # 输出对象的类型
dir() # 获得当前模块的属性列表
getattr(object, name, default) # 用于返回一个对象属性值。
hasattr() # 用于判断对象是否包含对应的属性
isinstance() # 判断一个对象是否是一个已知的类型
字典推导式
d = {key: value for (key, value) in iterable}
Python中单下划线和双下划线
>>> class MyClass():
... def __init__(self):
... self.__superprivate = "Hello"
... self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}
__foo __:一种约定,python内部的名字,用于区分其他用户自定义的命名。
_foo: 一种约定,用于表明 变量私有,因此无法通过import导入,其他方面与公有属性一样访问。
__foo: 表示私有成员,不能直接访问,但可以通过_class__foo进行访问,可以有效降低有意无意的修改。
字符串格式化:%和.format
"hi there %s" % name
%最烦人的是它无法同时传递一个变量和元组.
迭代器和生成器
迭代器利用iter()创建一个迭代器对象,利用next()输出迭代器的下一个元素。 此外,对于可迭代对象(列表、元组、字符串、集合、字典)等可以利用 for语句进行遍历。
把一个类作为一个迭代器使用需要在类中实现两个方法 __iter __() 与 __next __() 。
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
# StopIteration异常用于标识迭代的完成,防止出现无限循环的情况 在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
生成器利用 yied 关键字返回一个迭代器,即生成器时一个返回迭代器的函数,只用于迭代操作。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
import sys
def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
while True:
try:
print (next(f), end=" ")
except StopIteration:
sys.exit()
>>> L = [x*x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x*x for x in range(10))
>>> g
<generator object <genexpr> at 0x0000028F8B774200>
对于上述代码,将列表生成中的 [] 改成 ()后数据结构会发生改变,g改变为一个生成器。
通过列表生产可以直接创建一个列表,但是,受到内存限制,列表容量是有限的。因此可以采用生成器的方式:边循环、边计算。
*args and **kwargs
当你不确定你的函数里将要传递多少参数时你可以用*args传递元组参数(将参数打包成元组传递给函数);**kwargs传递关键字参数(将参数打包成dict传递给函数)。
*args必须在 **kwargs之前,且二者都在形式参数和默认值参数之后。
Python垃圾回收机制
python垃圾回收机制主要有引用计数、标记-清除、分代回收。
引用计数:PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,其ob_refcnt就会增加;当引用它的对象被删除时,其ob_refcnt就会减少;当引用计数变为0时,该对象生命结束,进行回收。
优点:简单、实时性。
缺点:维护引用计数消耗资源、循环引用
标记-清除:先按需分配,等到没有空闲内存时,从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫内存空间,把所有没标记的对象释放。
分代技术:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个代,垃圾回收频率随着代的存活时间的增大而减少。存活时间通常利用经过几次垃圾回收来度量。
python中is和==
is比较二者的地址是否一致,==比较二者的值是否一致。
python中的拷贝
python中的拷贝分为 浅拷贝和深拷贝。
浅拷贝:拷贝父对象(将父对象进行拷贝指向不同地址),但对于父对象中的子对象不会进行拷贝(对于子对象指向同一地址)。
深拷贝: 对父对象和子对象同时进行拷贝(指向不同地址)
import copy
a = [1, 2, 3, 4, ['a', 'b']] #原始对象
b = a #赋值,传对象的引用
c = copy.copy(a) #对象拷贝,浅拷贝
d = copy.deepcopy(a) #对象拷贝,深拷贝
a.append(5) #修改对象a
a[4].append('c') #修改对象a中的['a', 'b']数组对象
print 'a = ', a
print 'b = ', b
print 'c = ', c
print 'd = ', d
输出结果:
a = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
b = [1, 2, 3, 4, ['a', 'b', 'c'], 5]
c = [1, 2, 3, 4, ['a', 'b', 'c']]
d = [1, 2, 3, 4, ['a', 'b']]
python中open和with open
with:上下文管理器,在打开文件时使用,打开文件在进行读写时可能会出现异常状况,如果不用with自己要try,except,finally。with实现了finally中的f.close