分享一些面试遇到的python题目

题目本身是很常见的,但也容易忽略

re模块中match与search方法的不同

math与search方法,最明显的不同,就是匹配规则,match是从字符串第一个位置开始匹配,要求匹配的模式在字符串开始就必须满足,而search是从任意位置开始匹配,只要有满足模式的子字符串即可,如:

text="deabc"
m = re.match(r'(c)', text) # m为None
m = re.search(r'(c)', text) # 匹配成功,返回MatchObject
# match只有在text="cdeab"的情况下,才能匹配情况,必须字符串开头满足模式

Note:

match是作为字符串是否匹配成功的依据,只返回一次匹配结果,要想把所有的匹配结果进行返回,请使用findall

什么是lambda函数,其作用是什么

lambda函数是python的匿名函数,不需要使用def关键字进行定义的,它其实一个表达式,并不像函数是语句,lambda函数经常用来表示那些功能简单的函数,由于不需要传递给变量,在内存不存在栈对象,性能相对较好,lambda函数的格式如下:

lambda arg1, arg2, … argN: expression

lambda,经常也用在filter, map, reduce函数中

from functools import reduce
a = [1, 2, 3, 4, 5, 6]

filter_res = filter(lambda x: x % 2 == 0, a) 
print([a for a in filter_res]) # [2, 4, 6]

map_res = map(lambda x: x * 2, a)
print([a for a in map_res]) # [2, 4, 6, 8, 10, 12]

reduce_res = reduce(lambda x, y: x + y, a)
print(reduce_res) # 21

笔者这里用的是python3环境,reduce已经被移入functools模块

这道题目是关于继承

class Parent(object):
    x = 1
class Obj1(Parent):
    pass
class Obj2(Parent):
    pass
print Parent.x, Obj1.x, Obj2.x
Obj1.x = 2
print Parent.x, Obj1.x, Obj2.x
Parent.x = 3
print Parent.x, Obj1.x, Obj2.x
'''
输出结果为:
1 1 1
1 2 1
3 2 3
'''

看到结果,是不是觉得很诧异,首先x是类变量,属于Parent,打印dir(Obj1)时,你会发现,其实x也是它的属性。Obj1和Obj2都继承于Parent,因此第一行输出全是1。虽然继承Parent,但x其实本质属于Parent,但为啥子类没有却也可以输出值?这是因为,python类机制的原因,当子类不存在x时,它会搜索父类的x。每个类存在mro()的方法或mro魔术特性,可以访问,搜索机制的顺序。

print([x.__name__ for x in Obj1.mro()]) # ['Obj1', 'Parent', 'object']
print([x.__name__ for x in Obj2.__mro__]) # ['Obj2', 'Parent', 'object']

当获取属性或方法时,首先在子类中寻找,子类没有,在父类中寻找,最后是object
当Obj1.x = 2时,子类中存在x,会将2输出,父类中仍然是1, Obj2没有,继续输出父类的
当Parent.x = 3, 输出3 2 3,就会很容易明白了,父类x改变,Obj2输出3,而Obj1存在自己的x,输出2

请写出一个print_func_log装饰函数

@print_func_log
def add(x, y):
    print(x+y)

首先你得明白什么是装饰函数,装饰函数就是对目的函数进行装饰,在不改变函数时,增加新的东西。如果你需要标记add函数的执行前后,你可能会这样写:

def print_func_log(x, y):
    print('function:add' + ' start...')
    add(x, y)
    print('function:add' + ' end...')
# 这样实现对add函数标记,但是却只能通过执行print_func_log达到目的,当你的app存在存在大量add调用,你都得换成print_func_log
def print_func_log(func):
    def wrapper(x, y):
        print('function:'+func.__name__ + ' start...')
        func(x, y)
        print('function:'+func.__name__ + ' end...')
    return wrapper
add = print_func_log(add)
add(2, 3)
# 通过返回包装函数,实现对add函数包装,赋予add变量,通过变量执行函数,这样执行的函数就是add,python通过语法糖方式,将print_func_log(add)变为@print_func_log方式

实现一个有序字典类OrderedDict,支持dict所有方法,并且当调用items()方法时,返回有序的键值对元组列表,如:

d={},
d[‘a’] = 1
d[‘b’] = 2
d[‘c’] = 3
d.items() =>[(‘a’, 1), (‘b’, 2), (‘c’, 3)]

大家都知道,字典遍历是无序,并不会按添加键值对的顺序遍历,它的底层实现是树,不像列表内存是线性,但是要怎么样保持有序呢?这就需要在添加键值对时,用额外一个列表保持键的顺序,通过键在获取值,如:

d={}
key_list = []
d['a'] = 1
key_list.append('a')
d['b'] = 2
key_list.append('b')
d['c'] = 3
key_list.append('c')
for key in key_list:
    print(key, d[key])
# 这种方法是在类外做的,但是题目要求在类中,还要支持所有字典操作,那是不是想到首先定义一个继承字典的类呢,先不慌着写,看看系统为我们提供一个有序字典类,它是怎么样实现的

笔者使用的是python3, OrderedDirc类在collections模块中,使用如下:

from collections import OrderedDict

d = OrderedDict()
d['a'] = 1
d['b'] = 2
d['c'] = 3
print(d.items()) # odict_items([('a', 1), ('b', 2), ('c', 3)])
'''
查看OrderedDict
   'Dictionary that remembers insertion order' 记住插入顺序的字典
    # An inherited dict maps keys to values. 继承dict
    # The inherited dict provides __getitem__, __len__, __contains__, and get.
    # The remaining methods are order-aware.
    # Big-O running times for all methods are the same as regular dictionaries.

    # The internal self.__map dict maps keys to links in a doubly linked list.
    内部存在私有变量__map,它是个字典,将key映射为一个双向链表的链接
    # The circular doubly linked list starts and ends with a sentinel element.
    # The sentinel element never gets deleted (this simplifies the algorithm).
    # The sentinel is in self.__hardroot with a weakref proxy in self.__root.
    # The prev links are weakref proxies (to prevent circular references).
    # Individual links are kept alive by the hard reference in self.__map.
    # Those hard references disappear when a key is deleted from an OrderedDict.
'''

查看下源码,没仔细研究,毕竟我还是小白,就先不管牛人的代码,咱们自己实现个简单的,只要是能记住插入顺序的字典就可实现有序字典,现在写个简单demo:

class OrderedDict(dict):    
    '''
    初始化方法,并调用dict方法,使用额外的insert_order记录插入顺序
    '''
    def __init__(self):
        self.insert_order = []
        super(OrderedDict, self).__init__() 
    '''
    重写d['a']=1的方法,在插入键值对时,同时记录插入顺序
    '''
    def __setitem__(self, key, value):
        if key not in self.insert_order:
            self.insert_order.append(key)
        super(OrderedDict, self).__setitem__(key, value)    
    '''
    重写items方法,返回一个有序键值对元组列表
    '''
    def items(self):
        res = []
        for key in self.insert_order:
            res.append((key, self[key]))
        return res
d = OrderedDict()
d['a'] = 1
d['b'] = 2
d['c'] = 3
print(d.items) # [('a', 1), ('b', 2), ('c', 3)]

简单的还是很容易实现,而collections中的OrderedDict相对复杂,在效率上更高

实现一个map方法,不允许使用列表推导式(list-comprehension)

map的使用形式:

map(func, *iterables)
看到这个,觉得是不是就是对迭代对象的回调处理呢,用列表推导式,可以这样表示[func(x) for x in iterables],可以并没有这样简单,下面进行一些map的实践学习:

map对迭代对象处理完,返回一个列表

map(lambda x: x*2, [1, 2, 3, 4]) # [2, 4, 6, 8]

map还可以对多个对象进行并行处理,如:

map(lambda x, y, z: x*100+y*10+z, [1,2,3],[4,5,6],[7,8,9]) # [147, 258, 369]

map是不是很强大,可以对同一索引的元素
依照上面特性,实现自己的一个my_map

'''
当迭代对象多个,长度不同时,获取最短长度的对象
'''
def get_min_iterable_len(iterables):
    min_len = len(iterables[0])
    for iterable in iterables:
        if len(iterable) < min_len:
            min_len = len(iterable)
    return min_len
'''
获取同索引的一组值
'''             
def get_value(iterables, index):
    res = []
    for iterable in iterables:
        res.append(iterable[index])
    return res


def my_map(func, *iterables):
    if func is None:
        pass
    if len(iterables) == 0:
        pass
    min_len = get_min_iterable_len(iterables)
    for i in range(min_len):
        res = get_value(iterables, i)
        '''
        这里使用解包,将列表解包传递给函数,因为不确定函数的参数个数,采用yield返回
        '''
        yield func(*res)


map_res = my_map(lambda x, y: x + y, [1, 2, 3], [3, 4])
print([x for x in map_res])

这个遗憾的是,没采用多线程解决,主要最后获取全部结果时,线程那块我没考虑好

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值