python--面试题

面试题

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)来调用.
python--面试题

类变量和实例变量

类变量:是可在类的所有实例之间共享的变量。
实例变量:实例化后,每个实例单独拥有的变量

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

上一篇:【P1270 “访问”美术馆】题解


下一篇:【luogu P3172】选数(数学)(容斥)(DP)