python笔试面试题

1.什么是python?

  • 解释型语言。运行前不需要编译(同PHP、Ruby)
  • 动态类型语言。声明变量时,不需要说明变量类型。
  • 适合面向对象编程(OOP),支持通过组合与集成的方式定义类,python中没有访问说明符(public、private),设计依据是大家都是成年人了(哈哈~~)

说明:python中的组合与继承

组合:一个类的对象具备某种属性,该属性的值指向类外一个类的对象。

继承:描述类之间的从属关系。子类会用到父类中的一些特性。

class Course:                       #组合
    def __init__(self, name, period, price):
        self.name = name
        self.period = period
        self.price = price
 
    def tell_info(self):
        msg = """
         课程名:%s
         课程周期:%s
         课程价钱:%s
        """ % (self.name, self.period, self.price)
        print(msg)
 
class OldboyPeople:                                     #父类
    school = 'oldboy'
 
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex
 
class OldboyStudent(OldboyPeople):                子类
    def __init__(self, name, age, sex, stu_id):
        OldboyPeople.__init__(self, name, age, sex)
        self.stu_id = stu_id
 
    def choose_course(self):
        print('%s is choosing course' % self.name)
 
 
class OldboyTeacher(OldboyPeople):                     #子类
 
    def __init__(self, name, age, sex, level):
        OldboyPeople.__init__(self, name, age, sex)
        self.level = level
 
    def score(self, stu, num):
        stu.score = num
        print('老师[%s]为学生[%s]打分[%s]' % (self.name, stu.name, num))
 
# 创造课程
python = Course('python全栈开发', '5mons', 3000)
linux = Course('linux运维', '5mons', 800)
# python.tell_info()
# linux.tell_info()
 
# 创造学生与老师
stu1 = OldboyStudent('猪哥', 19, 'male', 1)
tea1 = OldboyTeacher('egon', 18, 'male', 10)
 
# 将学生、老师与课程对象关联/组合
stu1.course = python     #把课程python对象内存地址添加到stu1对象名称空间中,之后可以直接调用到course中的函数
print(stu1.__dict__)    
tea1.course = linux
 
stu1.course.tell_info()      直接用绑定方法调用组合里面的函数属性
tea1.course.tell_info()

组合:这里创建的course类中的对象python、linux分别是stu1和tea1的一个属性。

继承:这里的oldboyStudent和oldboyTeacher都是oldboyPeople的子类。它们继承了oldbotPeople类中的name、age和sex属性。

  • 函数是第一对象。它们可以被指定给变量,它们既可以返回函数类型,也可以接受函数作为输入。类也是第一对象。

第一对象:在计算机科学中指可以在执行期创造并作为参数传递给其他函数或存入一个变量的实体。具有以下特性:

  • 可以被存入变量或其他结构(list/dict/set...)

  • 可以被作为参数传递给其他函数

  • 可以被作为函数的返回值

  • 可以在执行期创造,而无需完全在设计期全部写出

  • 即使没有被系结至某一名称,也可以存在

这部分的细节内容可以参考博文:https://foofish.net/function-is-first-class-object.html

  • 代码编写快,但是运行速度比编译语言要慢。加入了基于c语言编写的扩展,可以优化代码,消除瓶颈。
  • 用途广泛:科学建模、大数据应用、网络应用、自动化等。
  • 程序员可以专注于算法和数据结构的设计,不用处理底层的细节。

2.补充缺失的代码。

def print_directory_contents(sPath):
    """
    这个函数接受文件夹的名称作为输入参数,
    返回该文件夹中文件的路径,
    以及其包含文件夹中文件的路径。

    """
    # 补充代码

答案:

def print_directionary_contents(sPath):
    import os
    for sChild in os.llistdir(sPath):
        sChildPath=os.path.join(sPath, sChild)
        if os.path.isdir(sChildPath):
            print_directory_contents(sChildPath)
        else:
            print sChildPath

注意:

  • 命名规范要统一。如果样本代码中能够看出命名规范,遵循其已有的规范。
  • 递归函数需要递归并终止。确保你明白其中的原理,否则你将面临无休无止的调用栈(callstack)。
  • 我们使用os模块与操作系统进行交互,同时做到交互方式是可以跨平台的。你可以把代码写成sChildPath = sPath + '/' + sChild,但是这个在Windows系统上会出错。
  • 熟悉基础模块是非常有价值的,但是别想破脑袋都背下来,记住Google是你工作中的良师益友。
  • 如果你不明白代码的预期功能,就大胆提问。
  • 坚持KISS原则!保持简单,不过脑子就能懂!

问题3:写出A0,A1,...,An的最终值

A0 = dict(zip(('a','b','c','d','e'),(1,2,3,4,5)))   ---->   A0={'a':1,'b':2,'c':3,'d':4,'e':5}     
A1 = range(10)                                      ---->   A1=[0,1,2,3,4,5,6,7,8,9]  #range(x)表示从0到x-1的整数
A2 = [i for i in A1 if i in A0]                     ---->   A2=[]
A3 = [A0[s] for s in A0]                            ---->   A3=[1,2,3,4,5]  #
A4 = [i for i in A1 if i in A3]                     ---->   A4=[1,2,3,4,5]  #同时在A1和A3中的数据
A5 = {i:i*i for i in A1}                            ---->   A5=[0,1,4,9,16,25,36,49,64,81]
A6 = [[i,i*i] for i in A1]           ---->   A6=[[0,0],[1,1],[2,4],[3,9],[4,16],[5,25],[6,36],[7,49],[8,64],[9,81]

问题4:Python和多线程(multi-threading)。这是个好主意码?列举一些让Python代码以并行方式运行的方法。

Python并不支持真正意义上的多线程。Python中提供了多线程包,但是如果你想通过多线程提高代码的速度,使用多线程包并不是个好主意。Python中有一个被称为Global Interpreter Lock(GIL)的东西,它会确保任何时候你的多个线程中,只有一个被执行。线程的执行速度非常之快,会让你误以为线程是并行执行的,但是实际上都是轮流执行。经过GIL这一道关卡处理,会增加执行的开销。这意味着,如果你想提高代码的运行速度,使用threading包并不是一个很好的方法。

不过还是有很多理由促使我们使用threading包的。如果你想同时执行一些任务,而且不考虑效率问题,那么使用这个包是完全没问题的,而且也很方便。但是大部分情况下,并不是这么一回事,你会希望把多线程的部分外包给操作系统完成(通过开启多个进程),或者是某些调用你的Python代码的外部程序(例如Spark或Hadoop),又或者是你的Python代码调用的其他代码(例如,你可以在Python中调用C函数,用于处理开销较大的多线程工作)。 


问题5:代码的输出结果

def f(x,l=[]):
    for i in range(x):
        l.append(i*i)
    print l

f(2)
f(3,[3,2,1])
f(3)

答案:

[0,1]

[3,2,1,0,1,4]

[0,1,0,1,4]

分析:f(2)运行结束后,l变为[0,1]; 在运行f(3,[3,2,1])时,l指向的是在新的内存空间创建的新的列表[3,2,1],并在其后append 0,1,4。在运行第三个函数时,l有继续指向第一次运行时在内存空间中创建的列表,此时该列表为[0,1],所以,第三个函数运行结束后,l为[0,1,0,1,4]


问题6:“猴子补丁”指的是什么,这种做法好吗?

“猴子补丁”指的是属性在运行时的动态替换。(在函数或对象定义好之后再去修改它们的行为)

这部分的内容有待加强了解(参考:https://blog.csdn.net/luo3300612/article/details/83986984


问题7:这两个参数是什么意思:*args**kwargs?我们为什么要使用它们?

函数的参数可以分为:位置参数,默认参数、可变参数、关键字参数和命名关键字参数。

位置参数:

def power(x, n):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

这里的x,n为位置参数,在调用函数时,传入的两个值按照位置一次赋值给参数x,n。

默认参数:

def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

与上一个例子相比,我们将n设置为默认参数,当我们调用函数时仅传入一个参数时,则将参数值传给x,而n=2。所以这里power(5,2)  <==>  power(5)。在定义函数时,要把必要参数写在前面,默认参数写在后面。同时,定义默认参数一定要指向不变对象。

可变参数:

可变参数在调用函数时,传入的参数个数是可变的。可变参数的表示形式为在变量名前加*,(*args即为可变参数)。

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

calc(1,2)   #sum=5
number=[1,2,3]
calc(*number)    #*number把number中的所有元素编程可变参数传入,sum=14

关键字参数:

关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。输入形式为**kwargs

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)
person('Bob',35,city='Beijing')      # print:  name: Bob age: 35 other: {'city': 'Beijing'
person('Bob',35)     #print:  name: Bob age: 35 other: {}

命名关键字参数:

命名关键字参数需要一个特殊分隔符**后面的参数被视为命名关键字参数。命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错。

def person(name, age, *, city, job):
    print(name, age, city, job)

这里的city和job均为命名关键字参数。

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

def person(name, age, *args, city, job):
    print(name, age, args, city, job)

在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。


问题8:下面这些是什么意思:@classmethod@staticmethod@property

这些都是装饰器(decorator)。装饰器是一种特殊的函数,要么接受函数作为输入参数,并返回一个函数,要么接受一个类作为输入参数,并返回一个类。@标记是语法糖(syntactic sugar),可以让你以简单易读得方式装饰目标对象。


问题9:

阅读下面的代码,它的输出结果是什么?

class A(object):
    def go(self):
        print "go A go!"
    def stop(self):
        print "stop A stop!"
    def pause(self):
        raise Exception("Not Implemented")

class B(A):
    def go(self):
        super(B, self).go()
        print "go B go!"

class C(A):
    def go(self):
        super(C, self).go()
        print "go C go!"
    def stop(self):
        super(C, self).stop()
        print "stop C stop!"

class D(B,C):
    def go(self):
        super(D, self).go()
        print "go D go!"
    def stop(self):
        super(D, self).stop()
        print "stop D stop!"
    def pause(self):
        print "wait D wait!"

class E(B,C): pass

a = A()
b = B()
c = C()
d = D()
e = E()

a.go()
b.go()
c.go()
d.go()
e.go()

a.stop()
b.stop()
c.stop()
d.stop()
e.stop()

a.pause()
b.pause()
c.pause()
d.pause()
e.pause()

结果:

a.go()
# go A go!

b.go()
# go A go!
# go B go!

c.go()
# go A go!
# go C go!

d.go()
# go A go!
# go C go!
# go B go!
# go D go!

e.go()
# go A go!
# go C go!
# go B go!

a.stop()
# stop A stop!

b.stop()
# stop A stop!

c.stop()
# stop A stop!
# stop C stop!

d.stop()
# stop A stop!
# stop C stop!
# stop D stop!


e.stop()
# stop A stop!

a.pause()
# ... Exception: Not Implemented

b.pause()
# ... Exception: Not Implemented

c.pause()
# ... Exception: Not Implemented

d.pause()
# wait D wait!

e.pause()
# ...Exception: Not Implemented

问题10:输出下面代码的结果

class Node(object):
    def __init__(self,sName):
        self._lChildren = []
        self.sName = sName
    def __repr__(self):
        return "<Node '{}'>".format(self.sName)
    def append(self,*args,**kwargs):
        self._lChildren.append(*args,**kwargs)
    def print_all_1(self):
        print self
        for oChild in self._lChildren:
            oChild.print_all_1()
    def print_all_2(self):
        def gen(o):
            lAll = [o,]
            while lAll:
                oNext = lAll.pop(0)
                lAll.extend(oNext._lChildren)
                yield oNext    #yield类似于迭代器,在运行过程中输出其后面的值
        for oNode in gen(self):
            print oNode

oRoot = Node("root")
oChild1 = Node("child1")
oChild2 = Node("child2")
oChild3 = Node("child3")
oChild4 = Node("child4")
oChild5 = Node("child5")
oChild6 = Node("child6")
oChild7 = Node("child7")
oChild8 = Node("child8")
oChild9 = Node("child9")
oChild10 = Node("child10")

oRoot.append(oChild1)
oRoot.append(oChild2)
oRoot.append(oChild3)
oChild1.append(oChild4)
oChild1.append(oChild5)
oChild2.append(oChild6)
oChild4.append(oChild7)
oChild3.append(oChild8)
oChild3.append(oChild9)
oChild6.append(oChild10)

# 说明下面代码的输出结果

oRoot.print_all_1()
oRoot.print_all_2()

运行结果:

oRoot.print_all_1()会打印下面的结果:

<Node 'root'>
<Node 'child1'>
<Node 'child4'>
<Node 'child7'>
<Node 'child5'>
<Node 'child2'>
<Node 'child6'>
<Node 'child10'>
<Node 'child3'>
<Node 'child8'>
<Node 'child9'>

oRoot.print_all_1()会打印下面的结果:

<Node 'root'>
<Node 'child1'>
<Node 'child2'>
<Node 'child3'>
<Node 'child4'>
<Node 'child5'>
<Node 'child6'>
<Node 'child8'>
<Node 'child9'>
<Node 'child7'>
<Node 'child10'>

问题11:简要描述Python的垃圾回收机制(garbage collection)。

  • Python在内存中存储了每个对象的引用计数(reference count)。如果计数值变成0,那么相应的对象就会小时,分配给该对象的内存就会释放出来用作他用。
  • 偶尔也会出现引用循环(reference cycle)。垃圾回收器会定时寻找这个循环,并将其回收。举个例子,假设有两个对象o1o2,而且符合o1.x == o2o2.x == o1这两个条件。如果o1o2没有其他代码引用,那么它们就不应该继续存在。但它们的引用计数都是1。
  • Python中使用了某些启发式算法(heuristics)来加速垃圾回收。例如,越晚创建的对象更有可能被回收。对象被创建之后,垃圾回收器会分配它们所属的代(generation)。每个对象都会被分配一个代,而被分配更年轻代的对象是优先被处理的。

问题12:将下面的函数按照执行效率高低排序。它们都接受由0至1之间的数字构成的列表作为输入。这个列表可以很长。一个输入列表的示例如下:[random.random() for i in range(100000)]。你如何证明自己的答案是正确的。

def f1(lIn):
    l1 = sorted(lIn)
    l2 = [i for i in l1 if i<0.5]
    return [i*i for i in l2]

def f2(lIn):
    l1 = [i for i in lIn if i<0.5]
    l2 = sorted(l1)
    return [i*i for i in l2]

def f3(lIn):
    l1 = [i*i for i in lIn]
    l2 = sorted(l1)
    return [i for i in l1 if i<(0.5*0.5)]

答案:

按执行效率从高到低排列:f2、f1和f3。要证明这个答案是对的,你应该知道如何分析自己代码的性能。Python中有一个很好的程序分析包,可以满足这个需求。

import cProfile
lIn = [random.random() for i in range(100000)]
cProfile.run('f1(lIn)')
cProfile.run('f2(lIn)')
cProfile.run('f3(lIn)')

为了向大家进行完整地说明,下面我们给出上述分析代码的输出结果:

>>> cProfile.run('f1(lIn)')
         4 function calls in 0.045 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.009    0.009    0.044    0.044 <stdin>:1(f1)
        1    0.001    0.001    0.045    0.045 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.035    0.035    0.035    0.035 {sorted}


>>> cProfile.run('f2(lIn)')
         4 function calls in 0.024 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.008    0.008    0.023    0.023 <stdin>:1(f2)
        1    0.001    0.001    0.024    0.024 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.016    0.016    0.016    0.016 {sorted}


>>> cProfile.run('f3(lIn)')
         4 function calls in 0.055 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.016    0.016    0.054    0.054 <stdin>:1(f3)
        1    0.001    0.001    0.055    0.055 <string>:1(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.038    0.038    0.038    0.038 {sorted}

 

参考:

https://www.cnblogs.com/wangchaowei/p/8184721.html  《Python笔试、面试 【必看】

https://blog.csdn.net/XiaoMaGe1996/article/details/80828864 《python之继承中组合用法与菱形继承关系查找法》

https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000   《廖雪峰的官方网站》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值