为python新手准备的基础知识小测验,分为数据类型及操作、函数、综合题三部分,总分100分。

[]内表示题目的分值。

数据类型及相关操作 [40]

  1. 如何判断一个变量是int类型?(此变量可能是任意类型) [3]
  2. // 和 / 有什么区别? [2]
  3. float类型的精度是什么意思,平时需要注意什么? [4]
  4. 0b1110, 0o16, 0xE,14 这四种写法分别是什么含义? 他们相等吗? [2]
  5. 看以下代码 [2]
a = "A" or "a" in ["b", "c"]
if a:
    print("there is a(or A) in list")

写出它的执行结果,分析其存在的问题。

  1. 看以下代码 [3]
s = "hello"
s.replace("e", "a")  # 替换e为a
print(s)

写出它的执行结果,分析其存在的问题。

  1. 看以下代码 [3]
a = "10"
print(a + 20)

你认为此段代码的运行结果是:

A. 30 B. 1020 C. 报错 D. 以上都不是
  1. 看以下代码 [5]
a = (2)
b = (2,)
c = 3,
d = {3}
e = {}

分别写出a,b,c,d,e5个变量的类型。

  1. 看以下代码 [3]
a = [1,2,3]
a.append(a)

请回答,这段代码

A. 会报错,列表不能append自己
B. 无报错,append后a长度为4
C. 无报错,append后a长度无限
D. 无报错,但是如果print(a)会报错
  1. list的很多方法是IN PLACE的,请解释IN PLACE的含义。 [5]
  2. dict的key和value的类型是否有特殊要求? [4]
  3. 看以下代码 [3]
a = [[1, 2]] * 2
a[0].append(3)

请写出a的值,并解释。

  1. 解释关键字del的作用,它和pop方法有什么区别? [3]
  2. 看以下代码 [4]
a = "a\n\t\\n"
b = r"a\\b\n"
print(list(a))
print(list(b))

写出其执行结果。

  1. 写出以下几行代码的结果 [4]
"100" < "101"
"100" < "99"
{1,2,3} == {1,3,2}
{1,2} < {1,2,3}

控制语句 [25]

  1. python的缩进是什么作用? [2]

  2. 关于if说法错误的有: [3]

    A. if后必须有else
    B. if不能嵌套使用
    C. if是循环语句
    D. if很有用
  3. 简述pass的作用,并举例说明。 [5]

  4. 简述return, break, continue的作用 [5]

  5. 以下代码

a = [1, 3, 4, 4, 5, 6, 8]
b = []
for i in a:
        if i % 2 == 0:
                b.append(2**i)
print(b)

考虑以下问题

1) 请将此代码改用while实现相同的功能。 [5]
2) 用list comprehension实现相同的功能。 [5]

函数及内置函数 [25]

  1. 以下函数 [5]
def foo(a, *b, **c):
    pass

实际调用foo(1,2,3,4,5,d=6,e=7)时,b和c的值分别是什么?

  1. 关于函数,以下说法错误的有: [2]

    A. 应尽可能的将代码分离为单独的函数,以便代码复用
    B. 规范的命名中,函数名用is和has开头的应返回bool类型的值
    C. 函数不需要写注释和文档,自己写的爽最重要
    D. 函数在return之后仍然可以继续执行
    E. return作用不大,可以用print替代
    F. 函数内部应尽量不使用全局变量,保持功能的独立和可移植性
  2. 关于递归,以下说法错误的有: [5]

    A. 递归函数十分强大,应尽可能多的使用
    B. 递归函数必须指定递归的终止条件
    C. 递归函数有最大层数(深度)限制
    D. 递归可以简单化问题,但是不用递归同样可以解决问题
  3. 简述str函数和repr函数的区别。 [3]

  4. 看以下代码 [5]

input = 50

请问此代码是否会报错,此代码这样写有哪些不好的影响?

  1. 类的方法(或者叫成员函数) 与 普通的函数有什么区别? [5]

综合题 [10]

打印九九乘法表

1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
....
def print_table():
    #代码写在这里

面向对象编程中我们经常需要获取或者设置对象的属性,可以有以下两种写法。

第一种,直接操作

class C(object):
    def __init__(self, x):
        self._x = x
c = C(10)
print(c._x)   #直接获取属性
c._x = 20     #直接设置属性

第二种,通过getter、setter

class C(object):
    def __init__(self, x):
        self._x = x
    def getx(self):
        return self._x
    def setx(self, x):
        self._x = x
c = C(10)
print(c.getx())   #通过getter函数
c.setx(20)        #通过setter函数

这两种方法中,明显第一种更简单直接。但是为什么还要有第二种方法呢?这是因为随着应用场景的变化,get和set会变得更复杂,比如需要统计get的次数,set的时候要设置最大值最小值等等,对第一种方法显然是很难适应这种需求。第二种方法就是所谓的数据封装

python中提供了property函数,可以让我们使用第一种的写法就达到第二种的效果。

class C(object):
    def __init__(self, x):
        self._x = x
    def getx(self):
        print("I am getx")
        return self._x
    def setx(self, x):
        print("I am setx")
        self._x = x
    x = property(getx, setx)
c = C(10)
print(c.x)      #会调用getx方法,打印"I am getx"
c.x = 20        #会调用setx方法,打印"I am setx"

用property函数,我们的代码就可以写得更简单,更优美,这也正好符合了python的哲学——Beautiful is better than ugly, Simple is better than complex.

gevent中使用了greenlet库,greenlet是python的一个代码执行单元,或者说是一个协程,不同的协程间可以进行切换。

greenlet的栈数据分为两部分,一部分存在栈上,另一部分存在堆上,较为新的数据存在堆上,相对旧的数据在栈上,这两部分数据都可能为空。greenlets是相互链接的,每一个都指向先前的greenlet,先前的greenlet在栈上的数据在此greenlet之上(更旧点)。当前运行的greenlet就是此链的第一个元素,最早的greenlet(main greenlet)就是此链的最后一个元素。数据完全在堆上的greenlet就可以不在此链上。此链与执行顺序无关,只与特定时间C栈属于哪一个greenlet有关。

greenlet C源码部分函数:

static PyGreenlet *green_create_main(void)
// 1. 获取当前线程的线程状态dict
// 2. PyType_GenericAlloc初始化greenlet
// 3. 初始化main greenlet变量

static PyObject* green_statedict(PyGreenlet* g)
// 如果g未开始,则取其parent,如此一直取到开始的greenlet,返回其run_info。

static int g_save(PyGreenlet* g, char* stop)
// 保存g的栈数据至stop处

static void GREENLET_NOINLINE(slp_restore_state)(void)
// 1. 恢复ts_target的栈数据
// 2. 恢复ts_target的stack_prev

static int GREENLET_NOINLINE(slp_save_state)(char *stackref)
// 保存ts_target->stack_stop下的greenlet的栈数据

static int g_switchstack(void)
// 根据提前设置好的全局变量作栈切换:
//   ts_current: 当前greenlet --强引用
//   ts_target: 切换的目标greenlet --弱引用
//   ts_passaround_args: 传递给ts_target的参数(tuple)
// 函数执行后返回结果同样用全局变量传递:
//   ts_origin: 原始greenlet --强引用
//   ts_current: 当前greenlet --弱引用
//   ts_passaround_args: 传递给ts_current的参数(tuple)
// 栈切换需要保证是原子操作,也就是说调用python代码是不允许的(除了少数安全的),因为全局变量是非常容易破坏的。

static PyObject *g_switch(PyGreenlet* target, PyObject* args, PyObject* kwargs)
// 1. 向上(parent)找到真正的target,忽略死掉的greenlet,必要的话启动一个greenlet。
// 2. g_switchstack
// 3. 返回参数

static PyObject *g_handle_exit(PyObject *result)
// 处理返回值,异常处理,或者用tuple返回

static int GREENLET_NOLINE(g_initialstub)(void* mark)
// 1. 初始化ts_target相关变量
// 2. g_switchstack
// 3. 调用ts_target(self)的run函数
// 4. run函数result返回
// 5. g_switch切换到其父greenlet

static PyObject* green_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
// PyBaseObject_Type.tp_new 创建新的greenlet

static int green_init(PyGreenlet *self, PyObject *args, PyObject *kwargs)
// 1. 从args, kwargs解析run parent
// 2. 设置self的run,和parent

static int kill_greenlet(PyGreenlet* self)
// 1. self必须在当前线程执行,否则在其线程dict中存del_key
// 2. g_switch切换到self的parent

static void green_dealloc(PyGreenlet* self)
// 1. kill_greenlet(self)
// 2. free memory

static PyObject* single_result(PyObject* results)
// return results

static PyObject *throw_greenlet(PyGreenlet *self, PyObject *typ, PyObject *val, PyObject *tb)
// 异常处理,然后退出self

static PyObject *green_switch(PyGreenlet *self, PyObject *args, PyObject *kwargs)
// g_switch

static PyObject *green_throw(PyGreenlet *self, PyObject *args)
// throw_greenlet

static int green_bool(PyGreenlet *self)
// 是否active

static PyObject *green_getdict(PyGreenlet *self, void *c)
// return dict

static int green_setdict(PyGreenlet *self, PyObject *val, void *c)
// set dict

static PyObject *green_getdead(PyGreenlet *self, void *c)
// return if dead

static PyObject *green_get_stack_saved(PyGreenlet *self, void *)
// return stack_saved size

static PyObject *green_getrun(PyGreenlet *self, void *c)
// return run_info

static int green_setrun(PyGreenlet *self, PyObject *nrun, void *c)
// set run info

static PyObject *green_getparent(PyGreenlet *self, void *c)
// return self->parent

static int green_setparent(PyGreenlet *self, PyObject *nparent, void *c)
// set parent

static PyObject *green_getframe(PyGreenlet *self, void *c)
// return top_frame

static PyObject *green_getstate(PyGreenlet *self)
// NULL

greenlet的执行流程

  1. import greenlet, 创建main greenlet
  2. 创建新的greenlet实例,设置run函数
  3. switch,切换状态,保存状态,执行,恢复状态

greenlet核心思想

保存栈数据,切换到新的代码段执行后,恢复栈数据,继续执行原代码

MORE