测试工程师社招-python面试题

基础知识

函数

函数是可重用的程序代码块,不仅可以实现代码的复用,还能实现代码的一致性。

Python执行def时,会创建一个函数对象,绑定到函数名变量上。

在函数内部改变全局变量的值,增加global关键字声明,nolocal声明外部函数的局部变量。

a = 20
def a():
    b = 10
    def inner():
        nonlocal b  #声明外部函数的局部变量
        print(b)
        b = 30
        global a    #声明全局变量
        a = 1000
    inner()
    print(b)
a()
print(a)

Locals()打印输出的局部变量(键值对的形式)

Globals()打印输出的全局变量(键值对的形式)

局部变量的查询和访问速度比全局变量快,优先使用。

函数的参数传递本质:从实参到形参的赋值操作1、对可变对象进行写操作,直接作用于原对象本身,比如:列表、字典等,直接修改这个对象 2、对不可变对象进行写操作,产生一个新的对象空间,比如:数值、元祖、字符串等,系统会新建一个对象

深拷贝和浅拷贝:1、浅拷贝:copy.copy()不拷贝子对象的内容,只拷贝子对象的引用2、深拷贝:copy.deepcopy()子对象的内存也全部拷贝一份,对子对象的修改不会影响源对象

import copy
def testCopy():
    '''测试浅拷贝'''
    a = [10, 20, [5, 6]]
    b = copy.copy(a)
    print("a", a)
    print("b", b)
    b.append(30)
    b[2].append(7)
    print("浅拷贝......")
    print("a", a)
    print("b", b)
def testDeepCopy():
    '''测试深拷贝'''
    a = [10, 20, [5, 6]]
    b = copy.deepcopy(a)
    print("a", a)
    print("b", b)
    b.append(30)
    b[2].append(7)
    print("深拷贝......")
    print("a", a)
    print("b", b)
testCopy()
print("*************")
testDeepCopy()

a [10, 20, [5, 6]]
b [10, 20, [5, 6]]
浅拷贝......
a [10, 20, [5, 6, 7]]
b [10, 20, [5, 6, 7], 30]
*************
a [10, 20, [5, 6]]
b [10, 20, [5, 6]]
深拷贝......
a [10, 20, [5, 6]]
b [10, 20, [5, 6, 7], 30]

传递不可变对象包含的子对象是可变的:修改可变对象,源对象会发生变化,a=(10,20,[5,6]) a[2][0] = 88 修改了源对象

参数的几种类型:1、位置参数,按照顺序传递形参实参一一对应,def f(a,b,c) f(1,2,3) 2、默认值参数,默认值参数必须位于普通参数的后面 def f1(a,b,c = 10,d = 20) f1(9,8,19) 3、命名参数,按照形参的名称传递参数,在调用的时候进行命名def f2(a,b,c) f2(c = 10,b = 20,a=22) 4、可变参数:*param,将多个参数收集到一个元祖对象中;**param,将多个参数收集到一个字典对象中 def f3(a,b,*c) f3(8,9,19,20) def f4(a,b,**d) f4(8,9,name=’zmx’,age=18) 5、强制命名参数:在可变参数后面增加新的参数,必须在调用的时候强制命名 def f5(*a,b,c) f5(2,3,4) f5(2,b = 3,c = 4)

lambda表达式和匿名函数:简单的定义函数的方法,lambda函数实际生成了一个函数对象

f = lambda a,b,c:a+b+c
print(f)
print(f(2,3,4))
#<function <lambda> at 0x01397E40>
#9
g = [lambda a:a*2,lambda b:b*3,lambda c:c*4]
print(g)  #三个函数对象
print(g[0](6),g[1](2),g[2](3))
#使用lambda函数对字典的值排序
d = {'a':4,'b':1,'c':2,'d':3}
D = sorted(d.items(),key = lambda x:x[1])
print(b)

递归函数(栈):自己调用自己,1、终止条件,递归什么时候结束2、递归步骤,n和n-1相关联

def fun(x,n):
    if n == 0:
        return 1
    else:
        return x*fun(x,n-1)
print(fun(3,4))

字典

键值对的无序可变序列,键任意不可变,值任意数据

创建:1、{}、dict()2、zip对象 dict(zip(k,v)) 3、fromkeys创建值为空的字典 dict.fromkeys()

k = {'name','age','job'}
v = {'gaoqi',18,'teacher'}
d = dict(zip(k,v))
print(d)
a = dict.fromkeys(k)
print(a)

访问:1、通过键访问 2、get,不存在返回空,或指定对象3、items()所有的键值对,keys()所有的键,values()所有的值4、len()键值对的个数 5、检测一个键是否在字典中,”name” in dict 返回true或者false

a = {'name': 'gaoqi', 'age': 18, 'job': 'programmer'}
print(a['name'])
print(a.get('sex','nan'))   #键不存在,返回指定的对象
print(a)
print(a.items())
print(a.keys())
print(a.values())
print(len(a))  #键值对的个数

字典元素添加、修改、删除:1、新增键值对,键已存在,覆盖旧的;不存在则新增2、update()将新字典所有键值对全部添加到旧字典上,key有重复则直接覆盖 3、删除

a = {'name':'gaoqi','age':18,'job':'programmer'}
a['address'] = '111'
b = {'a':1,'b':2}
a.update(b)
print(a)
del(a['name'])
b = a.pop('age')
print(a)
print(a.popitem())#随机删除和返回该键值对

序列解包用于字典时,默认对键进行操作

s = {'name':'gaoqi','age':18,'job':'teacher'}
name,age,job = s
name,age,job = s.items()  #键值对
name,age,job = s.values()   #值
print(name)

面试题:统计一个字符串中每个字符的个数

str = 'hello,world'
dict = {}
for i in str:
    if i in dict:
        dict[i] += 1
    else:
        dict[i] =1
for i in dict:
    print(i,dict[i])

元组

不可变,不能修改元组中的元素

创建:1、() 可省略 2、tuple()

访问:1、不能修改 2、a[1] a[:4]返回的仍是元组对象 3、sorted(tuple) 元组排序,并且返回一个新的元组对象

Zip:将多个列表对应位置的元素合成元组,并返回这个zip对象

a = [10,20,30]
b = [20,30,10]
d = zip(a,b)
print(list(d))

生成器推导式创建元组:可以通过生成器对象,转化成列表或元组,也可以使用生成器的__next__()方法进行遍历,元素访问结束之后,重新访问必须重新创建该生成器对象。T = (x*2 for x in range(5)) t.__next__() tuple(s)只能访问一次,第二次就空了需要在生成一次

集合

一个无序的不重复的元素序列,用花括号{}编写

创建(1、s = set() #空集合 2、s= {} type(s) #dict 3、s = {3,4,1,4,3} #3,4,1 可以去重)

访问(集合没有顺序没有索引 无法指定位置去访问,可以遍历)(s = {'a','b','c'} for i in s:print(i)

添加、删除 s.add() s.update()参数可以是列表、元祖、字典 s.remove('a')删除不存在的会报错 s.discard('3')元素不存在不会报错 s.clear()清空 s.pop()随机删除

集合运算:s1&s2 s1|s2 交集并集差集

面试题:

集合和列表区别1、集合无序,列表有序(访问索引)2、集合元素时唯一不重复,列表元素可以重复 3、集合中的元素不可变,而列表可变 4、集合 {} 列表[]

集合和字典区别1、集合无序不重复;字典有序可重复 2、集合只能存储不可变的对象,比如数字、str、元祖等 3、集合唯一 字典可重 4、集合不索引,字典可键

面向对象

面向对象和面向过程:1、都是解决问题的思维方式,都是代码组织的方式2、解决简单问题可以使用面向过程3、解决复杂问题:宏观上使用面向对象把握,微观处理上仍是面向过程

类:通过类定义数据类型的属性和方法,对象是类的实例,一个类创建对象时,每个对象会共享这个类的行为(类中定义的方法),但是会有自己的属性值。

class Student:
    def __init__(self,name,score):
        self.name = name    #实例属性
        self.score = score
    def say_score(self):    #实例方法
        print(self.name,'的分数是:',self.score)
s = Student('zmx',20)   #s是实例对象
s.say_score()
print(dir(s))   #获得对象的所有属性和方法
print(s.__dict__)  #对象的属性字典
#isinstance(对象,类型)  判断对象是不是指定的类型

__init__()方法:初始化创建好的对象,给实例属性赋值;不定义时,系统会提供一个默认的初始化方法;__new__()方法:用于创建对象,一般无需定义该方法;

Init和new区别:1、new负责对象的创建 init负责对象的初始化2、new创建对象时调用,会返回当前对象的一个实例;init创建完对象后调用,对当前对象的一些实例初始化,无返回值

实例属性:1、init方法中定义 2、本类其他实例方法self.实例对象名访问3、创建实例对象后,通过实例对象访问 obj = 类名();obj.实例属性名 = 值 #给已有属性赋值,也可以添加新的属性

实例方法:1、第一个参数self 2、调用时,不需要也不能给self传参

函数和方法的区别:1、本质,都用来完成一个功能 2、方法通过对象调用 3、方法定义时需要传递self,函数不需要

类对象:执行class语句时,会创建一个类对象

类属性:从属于类对象的属性,也称为类变量

class Student:
    company = 'sss'   #类属性
    count = 0
    def __init__(self,name,score):
        self.name = name    #实例属性
        self.score = score
        Student.count = Student.count+1
    def say_score(self):
        print("我的公司是:",Student.company)
        print(self.name,'的分数是:',self.score)
s = Student('zmx',20)
s.say_score()

类方法:从属于类对象的方法,装饰器@classmethod 1、装饰器2、第一个参数cls,指的是类对象本身 3、调用格式:类名.类方法名(参数列表),参数列表中不需要也不能给cls传值

class Student:
    company = 'sss'
    @classmethod
    def printCompany(cls):
        print(cls.company)
Student.printCompany()

静态方法:与类、对象无关的方法,和普通函数没有区别,只不过静态方法放到了类里面,需要通过类调用1、@staticmethod 2、调用静态方法:类名.静态方法名(参数列表)3、访问实例属性和实例方法导致错误

class Student:
    company = 'sss'
    @staticmethod
    def add(a,b):
        print('{0}+{1}={2}'.format(a,b,(a+b)))
        return a+b
Student.add(20,30)

__del__方法(析构函数)和垃圾回收机制:用于实现对象被销毁时所需的操作,比如释放对象占用的资源;python自动垃圾回收机制,当对象没有被引用时自动调用析构方法。

__call__方法和可调用对象:定义了call方法的对象成为可调用对象,该对象可以像函数一样被调用。

Python方法没有重载:如果在类中定义多个重名的方法,只有最优一个方法有效。

方法的动态性:可以动态的为类添加新的方法,或者动态的修改类的已有方法

class Person:
    def work(self):
        print("努力上班")
def play_game(self):
    print("玩游戏")
def work2(s):
    print("好好工作,努力上班")
Person.play = play_game
Person.work = work2
p = Person()
p.play()
p.work()

私有属性和私有方法(实现封装):1、两个下划线开头的属性是私有的private 2、类内部可以访问私有属性(方法)3、类外部不能直接访问私有属性(方法)4、类外部可以通过_类名__私有属性(方法)访问私有属性

class Employee:
    __company = "sss"  #私有类属性
    def __init__(self,name,age):
        self.name = name
        self.__age = age   #私有实例属性
    def say_company(self):
        print("我的公司是:",Employee.__company)   #类内部可以直接访问私有属性
        print(self.name,'年龄是:',self.__age)
        self.__work()
    def __work(self):   #私有实例方法
        print("工作!好好工作!")
p1 = Employee("sss",22)
print(p1.name)
print(dir(p1))
p1.say_company()
print(p1._Employee__age)  #直接访问私有属性

@property装饰器:将一个方法的调用变成属性调用,处理属性的读、写操作,做法不安全

面试题:装饰器是什么(本质是一个函数,别的函数添加装饰器可以在不修改代码的基础上添加额外的功能)

class Employee:
    @property
    def salary(self):
        return 30000
e = Employee()
print(e.salary)
print(type(e.salary))

封装:隐藏对象的属性和实现细节,只对外提供必要的方法,通过私有属性私有方法实现封装

class Employee:
    def __init__(self,name,salary):
        self.name = name
        self.__salary = salary


    @property    #相当于salary属性的getter方法
    def salary(self): 
        print("月薪为{0},年薪{1}".format(self.__salary,12*(self.__salary)))
        return self.__salary
    @salary.setter    #相当于salary属性的setter方法
    def salary(self,salary):
        if(0<salary<1000000):
            self.__salary = salary
        else:
            print("薪水录入错误")
e = Employee("zmx",1000)
print(e.salary)
e.salary = -200

继承:定义子类时,必须在其构造函数中调用父类的构造函数

class Person:
    def __init__(self,name,age):
        self.name = name
        self.__age = age
    def say_age(self):
        print(self.name,'年龄是:',self.__age)
class Student(Person):
    def __init__(self,name,age,score):
        self.score = score
        Person.__init__(self,name,age)
s = Student("zzz",12,30)
s.say_age()
print(dir(s))

1、成员继承:子类继承了父类除构造方法之外的所有成员 2、方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为重写 3、mro()或者__mro__可以输出这个类的继承层次结构 Student.mro()

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def say_age(self):
        print(self.name,'的年龄是:',self.age)
    def say_name(self):
        print("我是:",self.name)
class Student(Person):
    def __init__(self,name,age,score):
        self.score = score
        Person.__init__(self,name,age)
    def say_score(self):
        print(self.name,'的分数是:',self.score)
    def say_name(self):
        print("报告老师,我是",self.name)
s = Student("张三",17,89)
s.say_age()
s.say_score()
s.say_name()

__str__()方法,用于返回一个对于对象的描述,常用于print()方法)(把对象转换成一个字符串,一般用于print方法)

class Person:
    def __init__(self,name,age):
        self.name = name
        self.__age = age
    def __str__(self):
        return "名字是{0},年龄是{1}".format(self.name,self.__age)
p = Person("zzz",20)
print(p)

Super()获得父类定义:在子类中,如果想要获得父类的方法,可以通过super()来做。

class A:
    def say(self):
        print("A:",self)
        print("AAA")
class B(A):
    def say(self):
        super().say()
        print("BBB")
b = B()
b.say()

多态:同一个方法调用由于对象不同可能会产生不同的行为1、多态是方法的多态,属性没有多态 2、两个必要条件,继承和方法重写

class Animal:
    def shout(self):
        print("动物叫了一声")
class Dog(Animal):
    def shout(self):
        print("汪汪汪")
class Cat(Animal):
    def shout(self):
        print("喵喵喵")
def animalShout(a):
    if isinstance(a,Animal):
        a.shout()
animalShout(Dog())
animalShout(Cat())

面试题

1、幂等

多次请求所产生的影响和一次请求执行的影响效果相同

调用远程服务:成功、失败、超时,超时是未知的,转账超时做好幂等控制,发起重试,保证转账正常进行,又不会多转一笔(提交表单快速点击可能产生了两条一样的数据,前端重复提交)

方案:超时了,先查一下对应的记录,如果成功,走成功的流程;如果失败按失败处理

方案二:下游接口支持幂等,上游如果超时,发起重试即可(唯一索引、数据库主键)

2、内存溢出和内存泄露

内存溢出:内存不够,通常运行大型软件或者游戏的时候,软件和游戏所需要的内存远远超出了主机所安装的内存,这就是内存溢出

内存泄露:程序申请内存后,无法释放申请的内存空间

3、进程和线程

进程:资源分配的最小单位,独立的执行环境,拥有自己的内存空间和资源

线程:程序执行的最小单位,一个进程最少一个先后才能可以多个线程同时执行,共享进程的资源(通信)

进程类比公司,线程类比员工

4、栈的应用(括号匹配、函数递归调用)

5、app闪退和崩溃

手机、包、内存、第三方库、兼容

包本身,内存问题(不足)、第三方库(没有容错机制)、兼容、设备故障

编程错误、内存问题(内存溢出、内存泄露)程序崩溃、资源不足、第三方库(youbug,不兼容)、网络问题、数据异常、设备兼容问题、硬件故障

编程题

1、时间转秒

import datetime
#使用strptime(time,格式)将t.str解析为datetime对象
def time_to_seconds(time):
    t = datetime.datetime.strptime(time,"%H:%M:%S")
    seconds = (t.hour*3600)+(t.minute*60)+t.second
    return seconds
print(time_to_seconds("10:35:40"))

2、秒转时间

import datetime
#使用datetime下的timedelta(seconds)函数将秒转换成时间,并且格式化
def seconds_to_time(seconds):
    t = datetime.timedelta(seconds=seconds)
    time = str(t)
    return time
print(seconds_to_time(5445))

3、两个时间差

import datetime
#先转格式,在计算差,在total_seconds()转换秒
def time_diff(start_time,end_time):
    t1 = datetime.datetime.strptime(start_time,"%H:%M:%S")
    t2 = datetime.datetime.strptime(end_time,"%H:%M:%S")
    diff = t2-t1
    seconds = diff.total_seconds()
    return seconds
print(time_diff("01:30:45","02:15:30"))

4、统计每个字符串个数

s = 'hello world'
dict = {}
for i in s:
    if i in dict:
        dict[i]+=1
    else:
        dict[i]=1
for i in dict:
    print(dict[i],i)

5、判断回文串

def palidrome1(s):
    n = len(s)
    for i in range(int(n/2)):
        if s[i] != s[n-i-1]:
            return False
    return True
print(palidrome1("zmxmz"))

def palidrome2(s):
    return s == s[::-1]
print(palidrome2())

7、找一个数组中最大的数

def func(alist):
    n = len(alist)
    mid_index = alist[0]
    for i in range(0,n):
        if alist[i]<=alist[mid_index]:
            i+=1
        else:
            mid_index = i
    return alist[mid_index]

alist = [2,3,4,5,6,7,10]
print(func(alist))

8、冒泡排序

def bubble_sort(a):
    n = len(a)
    for k in range(n-1):
        count = 0
        for i in range(n - 1):
            if a[i] > a[i + 1]:
                a[i], a[i + 1] = a[i + 1], a[i]
                count += 1
        if count == 0:  #判断count的值是否等于0,如果等于0说明没有交换
                break
alist = [1,2,3,4,5]
bubble_sort(alist)
print(alist)

9、插入排序

def insert_sort(a):
    n = len(a)
    for j in range(1,n):   #两部分中的第二部分,从第二个元素开始到最后一个元素
        i = j
        while i>0:      #和有序列表中每个元素进行比较(从最后一个开始)
            if a[i]<a[i-1]:
                a[i],a[i-1] = a[i-1],a[i]    #如果当前元素比前一个元素小,进行交换
            else:   #否则已经是有序序列,不需要进行交换
                break
            i -= 1
alist=[54, 226, 93, 17, 77, 31, 44, 55, 20]
insert_sort(alist)
print(alist)

10、选择排序

def select_sort(alist):
    n = len(alist)
    for i in range(n-1): #循环一次,找起始位置,最后一个不用   O(n)
        min_index = i
        for j in range(i+1,n):   #利用索引  在剩余元素中找到最小的元素    O(n)
            if alist[j] < alist[min_index]:
                min_index = j
        if min_index!=i:   #如果最小索引变了,交换元素
            alist[i],alist[min_index] = alist[min_index],alist[i]
alist = [7,5,3,6,44,22,99,11]
select_sort(alist)
print(alist)

11、快速排序

def quick_sort(alist,start,end):
    #递归退出的条件
    if start>=end:
        return
    mid  = alist[start]
    low  = start
    high = end
    while low < high:
        #由右往左   alist[high]>mid  则high-1
        while low < high and alist[high] >mid:
            high -= 1
        alist[low] = alist[high]
        #由左往右  alist[low]<mid  则low+1
        while low < high and alist[low] <mid:
            low += 1
        alist[high] = alist[low]
    #将基准数放置在对应的位置
    alist[low] = mid
    #比基准数小的即左边的数据,要重复调用quick_sort()
    quick_sort(alist,start,low-1)
    #比基准数大的即右边的数据,要重复调用quick_sort()
    quick_sort(alist,low+1,end)
alist=[54, 226, 93, 17, 77, 31, 44, 55, 20]
quick_sort(alist,0,len(alist)-1)
print(alist)

12、归并排序

def merge_sort(alist):
    if len(alist)<=1:
        return alist
    num = len(alist)//2

    left = merge_sort(alist[:num])
    right = merge_sort(alist[num:])

    return merge(left,right)
def merge(left,right):
    l,r = 0,0
    result = []
    while l<len(left) and r < len(right):
        if left[l]<right[r]:
            result.append(left[l])
            l+=1
        else:
            result.append(right[r])
            r+=1
    result += left[l:]
    result += right[r:]

    return result

alist = [54,26,77,17,44,55]
sorted_alist = merge_sort(alist)
print(sorted_alist)

全部评论

相关推荐

2024-12-06 10:44
东北财经大学 运营
在拧螺丝的西红柿很热情:工作量数据化,你的实习我只看到了一个30%,比如总浏览量十万加,同比增长20%,用户复购率达到70%等等,自己根据你当时的工作情况挖掘吧
点赞 评论 收藏
分享
评论
8
55
分享
牛客网
牛客企业服务