python 常见面试题

1. python可第迭代对象、迭代器和生成器?

可迭代对象

可迭代对象,可作用于for循环的对象叫可迭代对象。
Python包含以下几种可迭代对象:
序列。包含:字符串、列表、元组、字典、集合
迭代器对象(iterator)
生成器函数(generator)
文件对象

迭代器

迭代器是用于迭代操作(for循环)的对象,它像列表一样可以迭代获取其中的每一个元素,任何实现了 __next__方法(python2是next)的对象都可以称为迭代器。
它与列表的区别在于,构建迭代器的时候,不想列表把所有元素一次加载到内存,而是以一种延迟计算(lazy evaluation)方式返回元素,这正是它的优点。当数据量比较大时列表所需要的内存远远大于迭代器,因为它并没有把所有元素装载到内存,而是等到调用next方法的时候才返回该元素。是一种按需调用的方式,for循环本质上就是不断地调用迭代器的next方法。
可作用于next()函数并能不断调用的对象叫迭代器对象

生成器

在python中有一种函数,用关键字yield来返回值,这种函数叫生成器函数,函数被调用时会返回一个生成器对象,生成器本质上还是一个迭代器,也是用在迭代操作中。yield对应的值在函数调用时不会立即返回,而是在调用next方法是才返回。
生成器是一种特殊的迭代器,生成器自动实现了“迭代器协议”(即__iter__和next方法),不需要再手动实现两方法。

生成器在迭代的过程中可以改变当前迭代值,而修改普通迭代器的当前迭代值往往会发生异常,影响程序的执行。

与迭代器相比,生成器没有那么多冗长的代码,而且性能上一样的高效。
生成器对象是一个存储算法并能记录上次位置的对象<惰性对象>

迭代器和生成器的区别

1 迭代器是一个更抽象的概念,任何对象,如果它的类有next方法和iter方法返回自己本身。对于string、list、dict、tuple等这类容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数,iter()是python的内置函数。iter()会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是python的内置函数。在没有后续元素时,next()会抛出一个StopIteration异常

2 生成器(Generator)是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数,只是在需要返回数据的时候使用yield语句。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)

区别:生成器能做到迭代器能做的所有事,而且因为自动创建了__iter__()和next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出StopIteration异常

2. 介绍一下生成器推导式和列表推导式?

推导式

推导式是从一个或者多个迭代器快速创建序列的一种方法。它可以将循环和条件判断结合,从而避免冗长的代码。推导式是典型的Python风格,会使用它代表你已经超过Python初学者的水平。

列表推导式(生成列表对象的生成式)

列表推导式生成列表对象,语法如下:
[表达式 for item in 可迭代对象 ]
或者:[表达式 for item in 可迭代对象 if 条件判断]
举例:

>>> [x for x in range(1,5)]
[1, 2, 3, 4]
>>> [x*2 for x in range(1,5)]
[2, 4, 6, 8]
>>> [x*2 for x in range(1,20) if x%5==0 ] # 这里的if条件可以有零个或多个
[10, 20, 30]
>>> [a  for  a  in "abcdefg"]
['a', 'b', 'c', 'd', 'e', 'f', 'g']

在看一个复杂的例子

>>> cells = [(row,col) for  row in range(1,10) for col in range(1,10) if row<=col ]  #可以使用两个循环
>>> for cell in cells:
		print(cell,end='|') # 这里加end是为了让每个cell打印完成后不换行,print()方法默认是打印完一次直接换行

输出结果为
(1, 1)|(1, 2)|(1, 3)|(1, 4)|(1, 5)|(1, 6)|(1, 7)|(1, 8)|(1, 9)|(2, 2)|(2, 3)|(2, 4)|(2, 5)|(2, 6)|(2, 7)|(2, 8)|(2, 9)|(3, 3)|(3, 4)|(3, 5)|(3, 6)|(3, 7)|(3, 8)|(3, 9)|(4, 4)|(4, 5)|(4, 6)|(4, 7)|(4, 8)|(4, 9)|(5, 5)|(5, 6)|(5, 7)|(5, 8)|(5, 9)|(6, 6)|(6, 7)|(6, 8)|(6, 9)|(7, 7)|(7, 8)|(7, 9)|(8, 8)|(8, 9)|(9, 9)|

字典推导式(生成字典对象的推导式)

字典的推导式生成字典对象,格式如下:
{key_expression : value_expression for 表达式 in 可迭代对象}

类似于列表推导式,字典推导也可以增加if条件判断、多个for循环。

例如:统计文本中字符出现的次数:

>>> my_text = ' i love you, i love python, i love study'
>>> char_count = {c:my_text.count(c)    for c in my_text}
>>> char_count
{' ': 9, 'i': 3, 'l': 3, 'o': 5, 'v': 3, 'e': 3, 'y': 3, 'u': 2, ',': 2, 'p': 1, 't': 2, 'h': 1, 'n': 1, 's': 1, 'd': 1}
集合推导式(生成集合对象的推导式)

集合推导式生成集合,和列表推导式的语法格式类似:
{表达式 for item in 可迭代对象 }
或者:{表达式 for item in 可迭代对象 if 条件判断}

>>> {x for x in range(1,100) if x%9==0}
{99, 36, 72, 9, 45, 81, 18, 54, 90, 27, 63}
生成器推导式

很多同学可能会问:“都有推导式,元组有没有?”,能不能用小括号呢?
先看代码

>>> (x for x in range(1,100) if x%9==0)
<generator object <genexpr> at 0x0000000002BD3048>

答案是可以用小括号,但是我们发现结果提示的是“一个生成器对象”。显然,元组是没有推导式的。

一个生成器只能运行一次。第一次迭代可以得到数据,第二次迭代发现数据已经没有了。

>>> gnt = (x for x in range(1,100) if x%9==0)
>>> for x in gnt:
	print(x,end=' ')
9 18 27 36 45 54 63 72 81 90 99 
>>> for x in gnt:
	print(x,end=' ')
	

第二次执行的结果就为空了。

3. is和== 的区别?

is 与 ==区别:
is :用于判断两个变量引用对象是否为同一个,既比较对象的地址(id)。
==:用于判断引用变量引用对象的值是否相等,默认调用对象的 eq()方法。

扩展: 整数缓存问题
Python仅仅对比较小的整数对象进行缓存(范围为[-5, 256])缓存起来,而并非是所有整数对象。需要注意的是,这仅仅是在命令行中执行,而在Pycharm或者保存为文件执行,结果是不一样的,这是因为解释器做了一部分优化(范围是[-5,任意正整数])。
总结
1、is 比较两个对象的 id 值是否相等,是否指向同一个内存地址;
2、== 比较的是两个对象的内容是否相等,值是否相等;
3、小整数对象[-5,256]在全局解释器范围内被放入缓存供重复使用;
4、is 运算符比 == 效率高,在变量和None进行比较时,应该使用 is

4. python2 和python3的区别?

  1. print函数:(Python3中print为一个函数,必须用括号括起来;Python2中print为class)
    Python 2 的 print 声明已经被 print() 函数取代了,这意味着我们必须包装我们想打印在小括号中的对象。

  2. 通过input()解析用户的输入:(Python3中input得到的为str;Python2的input的到的为int型,Python2的raw_input得到的为str类型)统一一下:Python3中用input,Python2中用row_input,都输入为str

幸运的是,在 Python 3 中已经解决了把用户的输入存储为一个 str 对象的问题。为了避免在 Python 2 中的读取非字符串类型的危险行为,我们不得不使用 raw_input() 代替。

  1. xrange模块:

在 Python 3 中,range() 是像 xrange() 那样实现以至于一个专门的 xrange() 函数都不再存在(在 Python 3 中xrange() 会抛出命名异常)。

在 Python 2 中 xrange() 创建迭代对象的用法是非常流行的。比如: for 循环或者是列表/集合/字典推导式。
  这个表现十分像生成器(比如。“惰性求值”)。但是这个 xrange-iterable 是无穷的,意味着你可以无限遍历。
  由于它的惰性求值,如果你不得不仅仅遍历它一次,xrange() 函数 比 range() 更快(比如 for 循环)。尽管如此,对比迭代一次,不建议你重复迭代多次,因为生成器每次都从头开始。

python 2.4 与 python 3.0 的比较
一、 print 从语句变为函数
原: print 1, 2+3
改为: print ( 1, 2+3 )
二、range 与 xrange
原 : range( 0, 4 ) 结果 是 列表 [0,1,2,3 ]
改为:list( range(0,4) )
原 : xrange( 0, 4 ) 适用于 for 循环的变量控制
改为:range(0,4)
三、字符串
原: 字符串以 8-bit 字符串存储
改为: 字符串以 16-bit Unicode 字符串存储

四、try except 语句的变化
原: try:

except Exception, e :

改为
try:

except Exception as e :

五、打开文件
原: file( … )
或 open(…)
改为:
只能用 open(…)
六、从键盘录入一个字符串
原: raw_input( “提示信息” )
改为: input( “提示信息” )
七、bytes 数据类型
A bytes object is an immutable array. The items are 8-bit bytes, represented by integers in the range 0 <= x < 256.
bytes 可以看成是“字节数组”对象,每个元素是 8-bit 的字节,取值范围 0~255。
由于在 python 3.0中字符串以 unicode 编码存储,当写入二进制文件时,字符串无法直接写入(或读取),必须以某种方式的编码为字节序列后,方可写入。
(一)字符串编码(encode) 为 bytes
例: s = “张三abc12”
b = s.encode( 编码方式)
# b 就是 bytes 类型的数据
# 常用的编码方式为 : “uft-16” , “utf-8”, “gbk”, “gb2312”, “ascii” , “latin1” 等
# 注 : 当字符串不能编码为指定的“编码方式”时,会引发异常
(二) bytes 解码(decode)为字符串
s = “张三abc12”
b = s.encode( “gbk”) # 字符串 s 编码为 gbk 格式的字节序列
s1 = b.decode(“gbk”) # 将字节序列 b以gbk格式 解码为字符串
# 说明,当字节序列不能以指定的编码格式解码时会引发异常
(三)使用方法举例

#coding=gbk
f = open("c:\\1234.txt", "wb")
s = "张三李四abcd1234"
# -------------------------------
# 在 python2.4 中我们可以这样写:
# f.write( s )
# 但在 python 3.0中会引发异常
# -------------------------------
b = s.encode("gbk")
f.write( b )
f.close()
input("?")

读取该文件的例子:

#coding=gbk
f = open("c:\\1234.txt", "rb")
f.seek(0,2) #定位至文件尾
n = f.tell() #读取文件的字节数
f.seek(0,0) #重新定位至文件开始处
b = f.read( n )
# ------------------------------
# 在 python 2.4 中 b 是字符串类型
# 要 python 3.0 中 b 是 bytes 类型
# 因此需要按指定的编码方式确码
# ------------------------------ 
s = b.decode("gbk")
print ( s )
# ------------------------------
# 在 python 2.4 中 可以写作 print s 或 print ( s )
# 要 python 3.0 中 必须写作 print ( s )
# ------------------------------ 
f.close()
input("?")

运行后应显示:

张三李四abcd1234

(四) bytes序列,一但形成,其内容是不可变的
例:

s="ABCD"
b=s.encode("gbk")
print b[0]       # 显示   65
b[0] = 66   
# 执行该句,出现异常: 'bytes' object does not support item assignment

八、 chr( K ) 与 ord( c )
python 2.4.2以前
chr( K ) 将编码K 转为字符,K的范围是 0 ~ 255
ord( c ) 取单个字符的编码, 返回值的范围: 0 ~ 255
python 3.0
chr( K ) 将编码K 转为字符,K的范围是 0 ~ 65535
ord( c ) 取单个字符的编码, 返回值的范围: 0 ~ 65535
九、 除法运算符
python 2.4.2以前
10/3 结果为 3
python 3.0
10 / 3 结果为 3.3333333333333335
10 // 3 结果为 3
十、字节数组对象 — 新增
(一) 初始化

    a = bytearray(   10 )
    # a 是一个由十个字节组成的数组,其每个元素是一个字节,类型借用 int
    # 此时,每个元素初始值为 0

(二) 字节数组 是可变的

    a = bytearray(   10 )
    a[0] = 25
    # 可以用赋值语句更改其元素,但所赋的值必须在 0 ~ 255 之间

(三) 字节数组的切片仍是字节数组
(四) 字符串转化为字节数组

# coding=gbk
s ="你好"
b = s.encode( "gbk")    # 先将字符串按某种“GBK”编码方式转化为 bytes
c = bytearray( b )      # 再将 bytes 转化为 字节数组

#  也可以写作
c = bytearray( "你好", "gbk")

(五) 字节数组转化为字符串

c = bytearray( 4 )
c[0] = 65 ; c[1]=66; c[2]= 67; c[3]= 68
s = c.decode( "gbk" )
print ( s )
# 应显示: ABCD 

(六) 字节数组可用于写入文本文件

#coding=gbk
f = open("c:\\1234.txt", "wb")
s = "张三李四abcd1234"
# -------------------------------
# 在 python2.4 中我们可以这样写:
# f.write( s )
# 但在 python 3.0中会引发异常
# -------------------------------
b = s.encode("gbk")
f.write( b )
c=bytearray( "王五","gbk")
f.write( c )
f.close()
input("?")

5. linux系统部署方式?

5.1 安装python3

由于linux系统一般默认自带的是python2,而现在的项目大多在python3上开发,python2和python3不兼容。所以第一步先安装python3以及其相关的依赖库。

1、 如果没有安装wget(wget命令用来从指定的URL下载文件),使用以下命令安装:

yum -y install wget

2、 访问 https://www.python.org/ftp/python/ 查找需要下载的python版本,譬如:Python-3.6.5.tgz ;或者使用window下载后传送到centos中;或者直接使用以下命令下载

wget https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tgz

​ PS:没有设置下载目录的话,文件会下载在当前目录

3、 安装编译器gcc:

yum install gcc

yum install gcc-c++

4、 执行解压:

tar -zxvf Python-3.6.5.tgz

5、 移动文件夹到指定目录/usr/local/ (5、6步可以同时省略,看个人习惯):

mv Python-3.6.5 /usr/local/

6、 进入到指定目录:

cd /usr/local/Python-3.6.5

7、 修改Setup:

vi Modules/Setup.dist

修改结果如下:

# Socket module helper for socket(2)  
_socket socketmodule.c timemodule.c  
  
# Socket module helper for SSL support; you must comment out the other  
# socket line above, and possibly edit the SSL variable:  
#SSL=/usr/local/ssl  
_ssl _ssl.c \  
-DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \  
-L$(SSL)/lib -lssl -lcrypto  

说明:最下面的3行,本来是被注释的,需要将注释去掉

8、 安装zlib(程序中的压缩或者解压缩函数都会用到这个库)依赖包:

yum install zlib*

9、安装ssl:

yum install openssl -y
yum install openssl-devel -y

10、执行配置:

./configure --prefix=/usr/local/python36 --with-ssl CFLAGS=-fPIC

11、执行编译:

make

PS:会比较耗时

12、执行编译安装:

make install

PS:会比较耗时

13、创建链接:

ln -s /usr/local/python36/bin/python3.6 /usr/bin/python3

ln -s /usr/local/python36/bin/pip3.6 /usr/bin/pip3

PS:记住 ln 中的 l 是 L 的小写,不是 i 的大写

centos中默认安装有python2.7,在系统中链接为 python2和python

我们可以删除现有python链接:

rm -rf /usr/bin/python

然后创建python3.6.5的链接为python

ln -s /usr/local/python3/bin/python3 /usr/bin/python

但是不推荐,因为很多命令都会依赖python,一旦修改为python3了,会有各种的错误产生

14、查看安装结果:

pip3 -V

python3 -V
5.2 python程序处理
5.2.1 通用配置修改

对于python程序的相关配置进行修改,例如:

  • 修改DEBUG为False,同时必须配置 ALLOWED_HOSTS ,内容是允许访问的域名列表,使用 * 表示通配符
  • 修改数据库连接为 生产环境 下使用的等
5.2.2 静态文件处理
  • 主要需要添加 STATIC_ROOT 配置,用于部署后,合并各个子应用下的静态文件
  • 部署后,使用 python manage.py collectstatic命令,合并各个子应用下的静态文件,然后在 处理静态文件的服务器中(如nginx)配置到该路径
5.2.3 python环境处理

在python项目的根目录,生成一个 requirements.txt

# 如果使用的第三方库比较少,那么可以手写一个 requirements.txt
# 如果使用的第三方库较多,那么就使用以下命令(开发环境 windos):
pip freeze > requirements.txt

# 拷贝上述的文件到服务器(linux)中,并且执行以下命令安装环境
pip3 install -r requirements.txt
5.2.4 复制项目文件到服务器

将项目拷贝linux系统中(譬如FTP),然后移动到 /var/www/ (项目最终存放的目录,可自定义)目录下

mv /ftp/childrenshop /var/www/

PS:拷贝完成后,记得执行合并static的命令python manage.py collectstatic

linux 服务器下 执行 python3 manage.py collectstatic

5.3 安装和使用uWSGI

uWSGI:python的web服务器,使用WSGI协议和django项目交互,使用uwsgi协议和nginx进行通信

  1. 使用 pip 安装uwsgi
pip3 install uwsgi==2.0.18 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

说明:

  • -i : 指定镜像源的网址,这里使用的豆瓣
  • –trusted-host : 信任 -i 指定的网站,不进行信任的话,会出现SSL错误
  1. 测试uwsgi
    编写一个测试的py文件
vi test.py

​ 文件内容如下:

#!/usr/bin/env python3
# coding:utf-8

import sys

def application(env,start_response):
    start_response('200 OK',[('Content-Type','text/html')])
    return 'Welcome to use uwsgi!!!'.encode()

​ 运行:

/usr/local/python36/bin/uwsgi --http 0.0.0.0:8997 --wsgi-f /var/www/childrenshop/test.py

新开一个连接窗口,查看端口使用详细信息:

注意:由于我们需要从本机访问linux的8001端口,因此需要设置linux的防火墙。

# 开放8997端口
firewall-cmd --zone=public --add-port=8997/tcp --permanent

# 删除端口
firewall-cmd --zone=public --remove-port=8997/tcp

# 重启防火墙
firewall-cmd --reload

# 查看防火墙状态
firewall-cmd --list-all​

在浏览器中,访问: http://192.168.134.101:8997 ,注意修改对应的ip地址
​ 即可查看到类似如下页面中显示:

Welcome to use uwsgi!!!
5.4 安装和使用nginx

Nginx:反向代理服务器,用于实现对用户请求的转发,转发给uWSGI,主要实现3个功能:

  • 反向代理
  • 动静分离
  • 负载均衡

6.介绍一下单例模式和工厂模式。

单例模式

单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点。

单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销。

单例模式有多种实现的方式,推荐重写__new__()的方法。
例如:

#单例模式
class MySingleton:
    __obj = None
    __init_flag = True

    def __new__(cls, *args, **kwargs):
        if cls.__obj == None:
            cls.__obj = object.__new__(cls)

        return cls.__obj

    def __init__(self,name):
        if MySingleton.__init_flag:
            print("init....")
            self.name = name
            MySingleton.__init_flag = False
# 测试代码
a = MySingleton("aa")
print(a)
b = MySingleton("bb")
print(b)
工厂模式

工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。
解耦
定义类或方法,让其功能越单一越好

#工厂模式
class CarFactory:
    def createCar(self,brand):
        if brand == "奔驰":
            return Benz()
        elif brand == "宝马":
            return BMW()
        elif brand == '比亚迪':
            return BYD()
        else:
            return "未知品牌,无法创建"


class Benz:
    pass


class BMW:
    pass


class BYD:
    pass

factory = CarFactory()
c1 = factory.createCar("奔驰")
c2 = factory.createCar("宝马")
print(c1)
print(c2)

7. 简述 进程、线程、协程的区别 以及应用场景?

程序(Program):是一个指令的集合。程序不能独立执行,只有被加载到 内存中,系统为它分配资源后才能执行。

进程(Process):如上所述,一个执行中的程序称为进程。 进程是系统分配资源的独立单位,每个进程占有特定的地址空间。 程序是进程的静态文本描述,进程是程序在系统内顺序执行的动态 活动。

线程(Thread):是进程的“单一的连续控制流程“。 线程是 CPU 调度和分配的基本单位,是比进程更小的能独立运行的 基本单位,也被称为轻量级的进程。 线程不能独立存在,必须依附于某个进程。一个进程可以包括多 个并行的线程,一个线程肯定属于一个进程。Java 虚拟机允许应用程序并发地 执行多个线程。举例:如一个车间是一个程序,一个正在进行生产任务的车间是一 个进程,车间内每个从事不同工作的工人是一个线程。

简单点说协程是进程和线程的升级版,进程和线程都面临着内核态和用户态 的切换问题而耗费许多切换时间,而协程就是用户自己控制切换的时机,不再需 要陷入系统的内核态. Python 里最常见的 yield 就是协程的思想!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值