Python 精选笔试面试习题—类继承、方法对象、包管理、闭包、可变类型作为默认参数、列表引用、sort与sorted、 append 和 extend、深拷贝和浅拷贝

1. 类继承

如下代码

class A(object):
    def show(self):
        print 'This is calss A'

class B(A):
    def show(self):
        print 'This is calss B'

obj = B()
obj.show()

如何才能调用类 A 的 show 方法呢?

obj.__class__ = A
obj.show()

__ class __ 方法指向了类对象,只用给他赋值类型 A,然后调用方法 show,但是用完了记得修改回来。

2. 方法对象

问题:为了让下面这段代码运行,需要增加哪些代码?

class A(object):
    def __init__(self,a,b):
        self.__a = a
        self.__b = b
    def myprint(self):
        print 'a=', self.__a, 'b=', self.__b


a1=A(10,20)
a1.myprint()

a1(80)

答案:为了能让对象实例能被直接调用,需要实现__ call __ 方法

class A(object):
    def __init__(self, a, b):
        self.__a = a
        self.__b = b
    def myprint(self):
        print 'a=', self.__a, 'b=', self.__b
    def __call__(self, num):
        print 'call:', num + self.__a

3. __ new __ 和 __ init __

下面这段代码输出什么?

class B(object):
    def fn(self):
        print 'B fn'
    def __init__(self):
        print "B INIT"


class A(object):
    def fn(self):
        print 'A fn'

    def __new__(cls,a):
            print "NEW", a
            if a>10:
                return super(A, cls).__new__(cls)
            return B()

    def __init__(self,a):
        print "INIT", a

a1 = A(5)
a1.fn()
a2=A(20)
a2.fn()

输出结果:

NEW 5
B INIT
B fn
NEW 20
INIT 20
A fn

使用__ new __ 方法,可以决定返回那个对象,也就是创建对象之前,这个可以用于设计模式的单例、工厂模式。__ init __ 是创建对象是调用的。

4. 默认方法

如下代码:

class A(object):
    def __init__(self,a,b):
        self.a1 = a
        self.b1 = b
        print 'init'
    def mydefault(self):
        print 'default'

a1 = A(10,20)
a1.fn1()
a1.fn2()
a1.fn3()

方法 fn1/fn2/fn3 都没有定义,怎样修改代码,使得没有定义的方法都调用 mydefault 函数,上面的代码应该输出

default
default
default

答案如下:

class A(object):
    def __init__(self,a,b):
        self.a1 = a
        self.b1 = b
        print 'init'
    def mydefault(self):
        print 'default'
    def __getattr__(self,name):
        return self.mydefault

a1 = A(10,20)
a1.fn1()
a1.fn2()
a1.fn3()

方法__ getattr __ 只有当没有定义的方法调用时,才是调用他。当 fn1 方法传入参数时,我们可以给mydefault 方法增加一个 *args 不定参数来兼容。

class A(object):
    def __init__(self,a,b):
        self.a1 = a
        self.b1 = b
        print 'init'
    def mydefault(self,*args):
        print 'default:' + str(args[0])
    def __getattr__(self,name):
        print "other fn:",name
        return self.mydefault

a1 = A(10,20)
a1.fn1(33)
a1.fn2('hello')
a1.fn3(10)

输出结果如下:

init
other fn: fn1
default:33
other fn: fn2
default:hello
other fn: fn3
default:10

5. 包管理

一个模块中有很多变量和函数,但有些变量和函数仅被期望于模块内部使用,而不是作为对外公有接口。一个模块对外的公有接口可以通过 __all__ 变量来定义,在导入模块的时候,通过 __all__ 变量可以指定可导入的部分。如下实例:

#hello.py模块代码
def printInfo():
    print('hello, world!')

def printSum(x,y):
    print(x+y)
#内部函数,不期望被导出
def __fun__():
    print("just for test!")    
#通过__all__变量定义模块的公有接口
__all__=['printInfo','printSum']

运行 test.py 模块

hello, world!
135
Traceback (most recent call last):
  File "D:\Users\xxx\testI\test.py", line 11, in <module>
    __fun__()
NameError: name '__fun__' is not defined

一个包里有三个模块,mod1.py, mod2.py, mod3.py,但使用 from demopack import * 导入模块时,如何保证只有 mod1、mod3 被导入了。

答案:增加__ init __.py 文件,并在文件中增加:

__all__ = ['mod1','mod3']

6. 闭包

写一个函数,接收整数参数n,返回一个函数,函数的功能是把函数的参数和n相乘并把结果返回。

In [8]: def fun(n):
   ...:     def g(val):
   ...:         return n * val
   ...:     return g
   ...: 
   ...: ff = fun(7)
   ...: print(ff(9))
   ...: 
63

7. 可变类型作为函数默认参数

下面代码输出结果是什么?

def extendList(val, list=[]):
    list.append(val)
    return list

list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')

print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3

输出结果为:

list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']

很多人都会误认为 list1=[10], list3=[‘a’],因为他们以为每次 extendList 被调用时,列表参数的默认值都将被设置为[].但实际上的情况是,新的默认列表只在函数被定义的那一刻创建一次。当 extendList 被没有指定特定参数list调用时,这组list的值随后将被使用。这是因为带有默认参数的表达式在函数被定义的时候被计算,不是在调用的时候被计算。
因此 list1 和 list3 是在同一个默认列表上进行操作(计算)的。而 list2 是在一个分离的列表上进行操作(计算)的。(通过传递一个自有的空列表作为列表参数的数值)。

extendList 的定义可以作如下修改。尽管,创建一个新的列表,没有特定的列表参数。

下面这段代码可能能够产生想要的结果。

def extendList(val, list=None):
  if list is None:
    list = []
  list.append(val)
  return list

通过上面的修改,输出结果将变成:

list1 = [10]
list2 = [123]
list3 = ['a']

8. 超出成员个数的切片

下面代码输出的结果是什么?

list = ['a', 'b', 'c', 'd', 'e']
print list[10:]

输出结果为:

[]

例如,尝试获取list[10]和之后的成员,会导致 IndexError。
然而,尝试获取列表的切片,开始的index超过了成员个数不会产生 IndexError,而是仅仅返回一个空列表。
这成为特别让人恶心的疑难杂症,因为运行的时候没有错误产生,导致bug很难被追踪到。

9. 列表的引用

有以下代码

1. list = [ [ ] ] * 5
2. list  # output?
3. list[0].append(10)
4. list  # output?
5. list[1].append(20)
6. list  # output?
7. list.append(30)
8. list  # output?

2,4,6,8行的输出结果如下:

[[], [], [], [], []]
[[10], [10], [10], [10], [10]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]
[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]

原因如下:

第一行的输出结果直觉上很容易理解,例如 list = [ [ ] ] * 5 就是简单的创造了5个空列表。
然而,理解表达式list=[ [ ] ] * 5的关键一点是它不是创造一个包含五个独立列表的列表,而是它是一个创建了包含对同一个列表五次引用的列表。
只有了解了这一点,我们才能更好的理解接下来的输出结果。

list[0].append(10) 将10附加在第一个列表上。

但由于所有5个列表是引用的同一个列表,所以这个结果将是:

[[10], [10], [10], [10], [10]]

同理,list[1].append(20)将20附加在第二个列表上。但同样由于5个列表是引用的同一个列表,所以输出结果现在是:

[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20]]

作为对比, list.append(30)是将整个新的元素附加在外列表上,因此产生的结果是:

[[10, 20], [10, 20], [10, 20], [10, 20], [10, 20], 30]

10. L.sort() 与 sorted() 的区别

L.sort(cmp=None, key=None, reverse=False)

sorted(iterable, cmp=None, key=None, reverse=False)
  • L.sort():该函数的三个参数和 sorted() 的后三个参数含义是一致的,而需要特别注意的是,该函数只适用于列表,而非任意可以迭代的对象。cmp 是比较函数,接受两个对象参数 x 和 y,返回 负数(x<y),0(x=y),正数(x>y)。
  • 该函数第一个参数 iterable 为任意可以迭代的对象,cmp 是比较函数,通常为lambda函数,key 是用于比较的关键字,reverse表示排序结果是否反转。
In [10]: aa = [5,4,3,2,1]

In [11]: aa.sort()

In [12]: aa
Out[12]: [1, 2, 3, 4, 5]

调用 sort() 之后会改变原来的表的结构顺序。

可以指定关键字排序

student = [['Tom', 'A', 20], ['Jack', 'C', 18], ['Andy', 'B', 11]]
student.sort(key=lambda student: student[2])

## 输出结果
[['Andy', 'B', 11], ['Jack', 'C', 18], ['Tom', 'A', 20]]

sorted() 可以应用于任意的可以迭代的对象,所以应用范围比 L.sort() 广泛的多,可以应用于字符串,元组,列表,字典等可迭代对象。

In [19]: s = "zyx"

In [20]: sorted(s)
Out[20]: ['x', 'y', 'z']

In [21]: s
Out[21]: 'zyx'

需要注意的是,该函数会返回一个排序后的列表,原有可迭代对象保持不变,这与 L.sort() 函数不同。然而,这会浪费较大的存储空间,尤其是数据量较大时。所以,在列表进行排序时,需要考虑是否需要保存原列表,如果无需保存原列表,则优先使用L.sort() 节省内存空间,提高效率

  • L.sort() 函数只适用于列表排序,而sorted()函数适用于任意可以迭代的对象排序。
  • L.sort() 函数排序会改变原有的待排序列表,而sorted()函数则不会改变。所以在使用列表进行排序时,需要考虑是否需要保存原列表,如果无需保存原列表,则优先使用L.sort() 节省内存空间,提高效率。
  • 两个函数通过定义 key 和 cmp 都可以完成排序,但是 key 的效率要远远高于 cmp,所以要优先使用 key 。

11. append 与 extend 方法有什么区别

append表示把某个数据当做新元素整体追加到列表的最后面,它的参数可以是任意对象。

extend 的参数必须是一个可迭代对象,表示把该对象里面的所有元素逐个地追加到列表的后面。

In [1]: x = [1,2,3]

In [2]: y = [4,5]

In [3]: x.append(y)

In [4]: x
Out[4]: [1, 2, 3, [4, 5]]

In [5]: x = [1,2,3]

In [6]: x.extend(y)

In [7]: x
Out[7]: [1, 2, 3, 4, 5]

参考:

http://www.bugcode.cn/PythonQuestions.html

https://segmentfault.com/a/1190000006265256

http://www.cnblogs.com/wilber2013/p/5178620.html

http://python.jobbole.com/86525/

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

http://www.cnblogs.com/Vito2008/p/5044251.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值