前言
昨天面了网易互娱的游戏研发工程师,回想下似乎问的问题并不多,整体也就 5 个问题,但每个问题展开之后里面的量好像也不少,现在整理一下,顺便解决下面试中没答上来的地方。
面试题
先是自我介绍。。。。。面试官了解到我对 Python 更加熟悉一些,于是问了比较多的 Python 相关的问题。
Python 中如何实现类成员的 public、private、protected
面试官:在 C++、Java中,定义一个类,其成员可以申明为 public、private、protected, 那么在Python 中如何实现类成员的 public、private、protected
答:Python 语言并不存在控制访问,所以无法直接定义类成员的 pubilc、private等属性,一般情况下是通过命名规约来达到这个效果,对于 private 的成员,一般命名以 _
开头,而 public 的则正常命名即可。
面试官:那对于双下划线__
开头的成员呢?
答:双下划线开头的成员感觉主要是用在类的继承的时候,如果不想子类直接访问到父类的这个成员,则可以以双下滑线开头。(修正,不仅仅是继承的时候,在该类中定义了双下划线开头的成员,实例化的对象也无法直接访问该成员)
面试官:如果我访问了这个双下划线开头的成员,会报什么错误?
这里我突然就愣住了,因为之前都没去尝试过,愣了一会儿之后,面试官给了些提示,如果你访问一个变量,但这个变量没有定义的话,那会报什么错误?
答:应该是变量不存在吧
面试官:那上面那个问题是报什么错误呢?
答:这么一说感觉应该是变量不存在
面试官:那再问问如果我非要访问这个双下滑线开头的成员,要怎么做?
这里真就不会了 orz
现在来补一下
'''
@Description:
@Author: 妄想
@Date: 2020-05-29 15:28:30
@LastEditTime: 2020-05-29 15:32:52
@LastEditors: 妄想
'''
class base:
def __init__(self):
self.public = 1
self._internal = 2
self.__private = 3
def public_method(self):
print('public method')
def _internal_method(self):
print('internal method')
def __private_method(self):
print('private method')
b = base()
print(b.public)
print(b._internal)
print(b.__private)
运行一下
那我该如何访问它呢?查了下资料,发现双下划线开头的成员,会被重命名,变成 _类名
+ 成员名,在这里就是b._base__private
print(b.public)
print(b._internal)
print(b._base__private)
函数也是类似
python 修饰器
面试官:了解过修饰器吗?
答:了解过
面试官:介绍下 Python 中修饰器(问题大概就是这个意思吧)
答:balabalabala。。。。(大致意思就是减少代码重复,封装成装饰器方便使用,类似于函数)
面试官:那一个装饰器的输入和输出是什么呢?
答:输入?可以是任意的参数吧,这个不是根据实际定义的吗?(这里我理解错了面试官的意思了- - ,因为之前在做 web 后端的时候,用了修饰器来做 token 验证,当时将 token 作为一个参数使用了)后面在面试官的引导下,反应过来了,输入和输出都是函数。
面试官:如果我不采用 @修饰器名称 的方式来调用修饰器,那该怎么做?
答:我印象中是直接当做函数调用就好了,但是好像并没成功 orz 。。
先来个一般写法
def foo(f):
def inner():
print('inner')
return f
return inner
@foo
def bar():
pass
bar()
会发现输出了 inner,然后我当时写了这样的调用
def foo(f):
def inner():
print('inner')
return f
return inner
def bar():
pass
foo(bar)
发现没输出 orz, 然后就过去了,后来仔细想想,虽说返回的是函数,但只是这个函数名啊,我并没有调用它哪来的输出啊,菜哭。。
改成这样就好了
bar = foo(bar)
bar()
# 或
foo(bar)()
python 迭代器
面试官:了解过迭代器吗?可以介绍下吗?
答:迭代器一般是用在对 Python 中的数据结构进行遍历,比如我现在有一个名为 li 的 List,我想对里面每个元素进行修改,那就需要遍历每个元素,这时通过 for number in li:
这条语句,便会生成一个迭代器,然后 number 为当前所在位置的元素。
面试官:那这跟直接索引遍历相比的好处是什么?
答:(这里凭感觉答的,也不清楚对不对)1. 按索引遍历的话首先需要知道这个数据结构的长度。2. 每次遍历的时候都需要通过索引来获取这个数据,而通过迭代器可直接获取到数据。
面试官:那有没有什么情况下是不能使用迭代器的呢?
答:这里我觉得存在删除操作的时候应该是不能使用迭代器的,如下面这段代码, 我想删除列表中大于 3 的元素
li = [1, 2, 3, 4, 5, 1, 4]
for i in li:
if i > 3:
li.remove(i)
print(li)
输出一下发现结果为 [1, 2, 3, 5, 1]
面试官:那添加的时候呢?
答:添加的情况没遇到过,不太清楚,但感觉应该也不太行。(个人感觉,一般来说添加是需要满足某种情况才进行添加,但是在迭代过程中进行添加,会导致迭代器需迭代的元素不断增加,如果操作不当可能会陷入死循环)。
C++ 结构体对齐
本以为结构体对齐很简单的,然而。。我天真了 - -
面试官:那接下来问问 C++ 方面的内容,了解过结构体的对齐吗?
答:了解过, balabalabala。。。。
面试官:那来算一下这个结构体的大小吧
struct Node
{
short a;
long b;
char c;
char d;
int e;
int f;
short i;
short j;
long k;
};
答:(算就算吧),算了一会儿,算出来一个 34 字节
面试官:怎么得出来的
答:首先 short 占 2 字节, 然后 long 占 8 字节, 后面加起来总共是 22 字节,对齐一下就是 24 字节,加起来总共 34 字节。(后来验证了下,发现自己错了 orz)
面试官:你确定这里第一个 short 只占 2 字节吗?
答:balabalabala。。。(我还觉得自己很对,后来下来验证了下发现自己打错了 orz)
面试官:这里操作系统是怎么读取数据的?一次能读两个字节吗?
答:(这里我已经懵逼了)emmmmm。。。不太清楚。。我记得这里应该是读取一个起始位置 + 偏移量来获取这个数据。
面试官:下去之后有兴趣再去了解了解吧
这里开始补充,发现 mingw64 long只占 4 个字节???
/*
* @Description:
* @Author: 妄想
* @Date: 2020-05-29 16:42:59
* @LastEditTime: 2020-05-29 16:54:49
* @LastEditors: 妄想
*/
#include
#include
using namespace std;
struct Node
{
short a;
long long b;
char c;
char d;
int e;
int f;
short i;
short j;
long long k;
};
// 2 + 8 + 22
// 34
int main(){
printf("%d\n", sizeof(Node));
printf("%d %d %d %d %d %d %d %d %d\n",
&Node::a, &Node::b, &Node::c, &Node::d, &Node::e, &Node::f, &Node::i, &Node::j, &Node::k);
printf("char:%d, short:%d, int:%d, long:%d, long long:%d\n", sizeof(char), sizeof(short), sizeof(int), sizeof(long), sizeof(long long));
return 0;
}
看一下输出
这个 long 只占 4 字节有点迷,没办法,就先改成 long long 吧。
可以发现,变量 b 的起始位置是 8 而不是 2,e 的起始位置是 20 而不是 18。
查了下相关博客:
- 结构体中元素需被放置在自身对齐大小的整数倍的位置上
- 如果结构体的大小不是所有元素中最大对齐大小的整数倍,则结构体对齐到最大元素对齐大小的整数倍,填充空间放置到结构体末尾
排序算法
面试官:接下来问问算法方面的东西吧,说一说快排的流程。
答:(很尴尬。。。已经很久没有接触过排序算法了,近期准备面试也没看过排序方面的东西,所以想了一段时间,好歹还是想起来了) balabalabala。。。。
面试官:那时间复杂度呢?
答:最差 \(O(n^2)\) ,平均 \(O(n\log n)\)
面试官:什么情况下最差呢?
答:数组完全逆序排序的时候
面试官:那怎么选取主元能使得快排的性能最好呢?
答:(这个之前在算法导论上看到过,然而已经忘了。。。想了想,如果说每次划分正好在数组的正中间,那么可达一个较优的情况所以就答了中位数)
面试官:那再问问其他问题,有了解过其他最差情况不会像快排这样的排序算法吗?
答:(这里感觉面试官问的应该是稳定的排序算法有哪些,当时能想起来的就是归并排序结果一不小心答成分治排序 - -)
补充:从别的地方拿来的图
算法题
写了道链表翻转,结果很尴尬,忘了将翻转后的链表最后一个元素的 next 设置为空,导致一直超时 orz 。。。。。过程中边 debug 边跟面试官讨论,最后改出来了。。害,太久没手写过数据结构了,这么一道简单的题还写出 bug。。。哭了
提问
问了下想到网易做游戏开发需要学些什么(毕竟当年是想做游戏开发才选的计算机,而且大一刚入学的目标就是网易游戏,虽然后来搞了近两年机器学习。。),面试官说是并没有太指望应届生有游戏开发相关的知识,要求就是基础要扎实,能够做到他们交给我们的东西我们能学会。(不说了,接着补基础去了。。。)
小节
一次体验极好的面试,总共面了一个小时多一点,过程中有答不上来的地方面试官会慢慢引导自己,结束之后感觉好像答的还不错,梳理一下发现问题还是有点多 orz。。面试之前看了不少网易游戏研发的面经,照着面经准备了几天,发现被问到的面经上都没出现,真就全靠平时积累。。。看来还有好多地方需要补一补。。说是一周内会出结果,只能看命了。。
从三月份开始投算法岗,一直到之前改投开发岗,挂了无数次,看着周围一些同学都拿了不少 offer,压力有点大。。