python:练习

文件与目录

有一个jsonline格式的文件file.txt大小约为10K

def get_lines():
	with open('file.txt','rb') as f:
		return f.readlines()

if __name__ == '__main__':
	for e in get_lines():
			process(e) # 处理每一行数据

现在要处理一个大小为10G的文件,但是内存只有4G,如果在只修改get_lines 函数而其他代码保持不变的情况下,应该如何实现?需要考虑的问题都有那些?

回答

  • 听起来你的代码是I/O绑定的。这意味着,如果您将90%的时间用于从磁盘读取,多進程将不会有任何帮助
  • 不过,值得检查的是,您是否确实受到I/O限制,而不是仅仅猜测。运行程序,查看CPU使用率是否接近0%或接近100%或核心。
  • 由于您使用了Python2.x,Python依赖于C stdio库来猜测一次要缓冲多少,因此强制它缓冲更多可能是值得的。最简单的方法是对一些大型bufsize使用readlines(bufsize)。(你可以尝试不同的数字并测量它们,看看峰值在哪里。根据我的经验,通常64K-8MB之间的数据都差不多,但取决于你的系统,这可能会有所不同,特别是如果你正在读取一个网络文件系统,该文件系统的吞吐量很高,但延迟却很可怕,这会影响实际物理驱动器的吞吐量和操作系统的缓存。
bufsize = 65536
with open(path) as infile: 
    while True:
        lines = infile.readlines(bufsize)
        if not lines:
            break
        for line in lines:
            process(line)
  • 同时,假设您使用的是64位系统,您可能希望首先尝试使用mmap而不是读取文件。这当然不能保证会更好,但可能会更好,具体取决于您的系统。例如:
with open(path) as infile:
    m = mmap.mmap(infile, 0, access=mmap.ACCESS_READ)

回答

from mmap import mmap

def get_lines(fp):
	with open(fp,"r+") as f:
		m = mmap(f.fileno(), 0)
		tmp = 0
		for i, char in enumerate(m):
			if char==b"\n":
				yield m[tmp:i+1].decode()
				tmp = i+1

if __name__=="__main__":
	for i in get_lines("fp_some_huge_file"):
	print(i)

要考虑的问题有:

  • 内存只有4G无法一次性读入10G文件,需要分批读入分批读入数据要记录每次读入数据的位置。
  • 分批每次读取数据的大小,太小会在读取操作花费过多时间

https://stackoverflow.com/questions/30294146/fastest-way-to-process-a-large-file

遍历文件夹

import os

def print_directory_contents(sPath):
"""
这个函数接收文件夹的名称作为输入参数
返回该文件夹中文件的路径
以及其包含文件夹中文件的路径
"""
	for s_child in os.listdir(s_path):
		s_child_path = os.path.join(s_path, s_child)
		
	if os.path.isdir(s_child_path):
		print_directory_contents(s_child_path)
	else:
		print(s_child_path)

设计实现遍历目录与子目录,抓取.pyc文件

第一种方法:

import os

def get_files(dir,suffix):
	res = []
	for root,dirs,files in os.walk(dir):
		for filename in files:
			name,suf = os.path.splitext(filename)
			if suf == suffix:
				res.append(os.path.join(root,filename))
	print(res)

get_files("./",'.pyc')

第二种方法:

import os

def pick(obj):
	if obj.endswith(".pyc"):
		print(obj)

def scan_path(ph):
	file_list = os.listdir(ph)
	for obj in file_list:
		if os.path.isfile(obj):
			pick(obj)
		elif os.path.isdir(obj):
			scan_path(obj)

if __name__=='__main__':
	path = input('输入目录')
	scan_path(path)

第三种方法

from glob import iglob

def func(fp, postfix):
	for i in iglob(f"{fp}/**/*{postfix}", recursive=True):
		print(i)

if __name__ == "__main__":
	postfix = ".pyc"

func("K:\Python_script", postfix)

统计一个文本中单词频次最高的10个单词?

import re

# 方法一
def test(filepath):
	distone = {}
	with open(filepath) as f:
	for line in f:
		line = re.sub("\W+", " ", line)
		lineone = line.split()
	for keyone in lineone:
		if not distone.get(keyone):
			distone[keyone] = 1
		else:
			distone[keyone] += 1
	
	num_ten = sorted(distone.items(), key=lambda x:x[1], reverse=True)[:10]
	num_ten =[x[0] for x in num_ten]
	return num_ten

# 方法二
# 使用 built-in 的 Counter 里面的 most_common
import re
from collections import Counter

def test2(filepath):
	with open(filepath) as f:return list(map(lambda c: c[0], Counter(re.sub("\W+", " ",f.read()).split()).most_common(10)))

模块与包

输入日期, 判断这一天是这一年的第几天?

import datetime

def dayofyear():
	year = input("请输入年份: ")
	month = input("请输入月份: ")
	day = input("请输入天: ")
	date1 = datetime.date(year=int(year),month=int(month),day=int(day))
	date2 = datetime.date(year=int(year),month=1,day=1)
	return (date1-date2).days+1

打乱一个排好序的list对象alist?

import random
alist = [1,2,3,4,5]
random.shuffle(alist)
print(alist)

Python中类方法、类实例方法、静态方法有何区别?

即采用 @classmethod 修饰的方法为类方法;采用 @staticmethod 修饰的方法为静态方法;不用任何修改的方法为实例方法。

类实例方法

  • 通常情况下,在类中定义的方法默认都是实例方法。
class CLanguage:
    #类构造方法,也属于实例方法
    def __init__(self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    # 下面定义了一个say实例方法
    def say(self):
        print("正在调用 say() 实例方法")
  • 实例方法最大的特点就是,它最少也要包含一个 self 参数,用于绑定调用此方法的实例对象(Python 会自动完成绑定)。实例方法通常会用类对象直接调用,例如:
clang = CLanguage()
clang.say()
  • 当然,Python 也支持使用类名调用实例方法,但此方式需要手动给 self 参数传值。例如:
#类名调用实例方法,需手动给 self 参数传值
clang = CLanguage()
CLanguage.say(clang)

类方法

  • Python 类方法和实例方法相似,它最少也要包含一个参数,只不过类方法中通常将其命名为 cls,Python 会自动将类本身绑定给 cls 参数(注意,绑定的不是类对象)。也就是说,我们在调用类方法时,无需显式为 cls 参数传参。
  • 和实例方法最大的不同在于,类方法需要使用@classmethod修饰符进行修饰,例如:
class CLanguage:
    #类构造方法,也属于实例方法
    def __init__(self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    #下面定义了一个类方法
    @classmethod
    def info(cls):
        print("正在调用类方法",cls)
  • 类方法推荐使用类名直接调用,当然也可以使用实例对象来调用(不推荐)。
#使用类名直接调用类方法
CLanguage.info()
#使用类对象调用类方法
clang = CLanguage()
clang.info()
  • 运行结果为:
正在调用类方法 <class '__main__.CLanguage'>
正在调用类方法 <class '__main__.CLanguage'>

类静态方法

  • 静态方法,其实就是我们学过的函数,和函数唯一的区别是,静态方法定义在类这个空间(类命名空间)中,而函数则定义在程序所在的空间(全局命名空间)中。
  • 静态方法没有类似 self、cls 这样的特殊参数,因此 Python 解释器不会对它包含的参数做任何类或对象的绑定。也正因为如此,类的静态方法中无法调用任何类属性和类方法。
  • 静态方法需要使用@staticmethod修饰,例如:
class CLanguage:
    @staticmethod
    def info(name,add):
        print(name,add)
  • 静态方法的调用,既可以使用类名,也可以使用类对象,例如:
#使用类名直接调用静态方法
CLanguage.info("C语言中文网","http://c.biancheng.net")
#使用类对象调用静态方法
clang = CLanguage()
clang.info("Python教程","http://c.biancheng.net/python")

在实际编程中,几乎不会用到类方法和静态方法,因为我们完全可以使用函数代替它们实现想要的功能,但在一些特殊的场景中(例如工厂模式中),使用类方法和静态方法也是很不错的选择。

python新式类和经典类的区别?

  • a. 在python里凡是继承了object的类,都是新式类

  • b. Python3里只有新式类

  • c. Python2里面继承object的是新式类,没有写父类的是经典类

  • d. 经典类目前在Python里基本没有应用

  • e. 保持class与type的统一对新式类的实例执行a.class与type(a)的结果是一致的,对于旧式类来说就不一样了。

  • f.对于多重继承的属性搜索顺序不一样新式类是采用广度优先搜索,旧式类采用深度优先搜索。

可变类型和不可变类型

1,可变类型有list,dict.不可变类型有string,number,tuple.
2,当进行修改操作时,可变类型传递的是内存中的地址,也就是说,直接修改内存中的值,并没有开辟
新的内存。

  • 3,不可变类型被改变时,并没有改变原内存地址中的值,而是开辟一块新的内存,将原地址中的值复制
    过去,对这块新开辟的内存中的值进行操作。

is和==有什么区别?

  • is:比较的是两个对象的id值是否相等,也就是比较俩对象是否为同一个实例对象。是否指向同一个内
    存地址

  • == : 比较的两个对象的内容/值是否相等,默认会调用对象的eq()方法

设计模式

python如何实现单例模式?请写出两种实现方式?

  • 第一种:使用装饰器
def singleton(cls):
	instances = {}
	def wrapper(*args, **kwargs):
		if cls not in instances:
			instances[cls] = cls(*args, **kwargs)
		return instances[cls]
	return wrapper

@singleton
class Foo(object):
	pass

foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2) # True
  • 第二种方法:使用基类
    New 是真正创建实例对象的方法,所以重写基类的new 方法,以此保证创建对象的时候只生成一个实例
class Singleton(object):
	def __new__(cls, *args, **kwargs):
		if not hasattr(cls, '_instance'):
			cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
		return cls._instance

class Foo(Singleton):
	pass

foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2) # True
  • 第三种方法:元类
    元类是用于创建类对象的类,类对象创建实例对象时一定要调用call方法
    因此在调用call时候保证始终只创建一个实例即可,type是python的元类
class Singleton(type):
	def __call__(cls, *args, **kwargs):
		if not hasattr(cls, '_instance'):
			cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
		return cls._instance

# Python2
class Foo(object):
	__metaclass__ = Singleton

# Python3
class Foo(metaclass=Singleton):
	pass

foo1 = Foo()
foo2 = Foo()
print(foo1 is foo2) # True

算法

两数之和

给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。你可以假设每个输入只对应一种答
案,且同样的元素不能被重复利用。示例:给定nums = [2,7,11,15],target=9 因为 nums[0]+nums[1] =
2+7 =9,所以返回[0,1]

class Solution:
	def twoSum(self,nums,target):
		"""
		:type nums: List[int]
		:type target: int
		:rtype: List[int]
		"""
		d = {}
		size = 0
		while size < len(nums):
			if target-nums[size] in d:
				if d[target-nums[size]] <size:
					return [d[target-nums[size]],size]
				else:
					d[nums[size]] = size
					size = size +1

solution = Solution()
list = [2,7,11,15]
target = 9
nums = solution.twoSum(list,target)
print(nums)
class Solution(object):
	def twoSum(self, nums, target):
		for i in range(len(nums)):
			num = target - nums[i]
			if num in nums[i+1:]:
				return [i, nums.index(num,i+1)]

反转一个整数,例如-123 --> -321

class Solution(object):

	def reverse(self,x):
		if -10<x<10:
		return x
		str_x = str(x)
		if str_x[0] !="-":
			str_x = str_x[::-1]
			x = int(str_x)
		else:
			str_x = str_x[1:][::-1]
			x = int(str_x)
			x = -x
		return x if -2147483648<x<2147483647 else 0

if __name__ == '__main__':
	s = Solution()
	reverse_int = s.reverse(-120)
	print(reverse_int)

字符串"123" 转换成 123 ,不使用内置api,例如 int()

方法一: 利用 str 函数

def atoi(s):
	num = 0
	for v in s:
		for j in range(10):
			if v == str(j):
				num = num * 10 + j
	return num

方法二: 利用 ord 函数

def atoi(s):
	num = 0
	for v in s:
		num = num * 10 + ord(v) - ord('0')
		return num

方法三: 利用 eval 函数

def atoi(s):
	num = 0
	for v in s:
		t = "%s * 1" % v
		n = eval(t)
		num = num * 10 + n
return num

方法四: 结合方法二,使用 reduce ,一行解决

from functools import reduce
def atoi(s):
	return reduce(lambda num, v: num * 10 + ord(v) - ord('0'), s, 0)

一行代码实现1-100之和

count = sum(range(0,101))
print(count)

求出列表所有奇数并构造新列表

a = [1,2,3,4,5,6,7,8,9,10]
res = [ i for i in a if i%2==1]
print(res)

用一行python代码写出1+2+3+10248

from functools import reduce
#1.使用sum内置求和函数
num = sum([1,2,3,10248])
print(num)
#2.reduce 函数
num1 = reduce(lambda x,y :x+y,[1,2,3,10248])
print(num1)

请写出一个函数满足以下条件

该函数的输入是一个仅包含数字的list,输出一个新的list,其中每一个元素要满足以下条件:
1、该元素是偶数
2、该元素在原list中是在偶数的位置(index是偶数)

def num_list(num):
	return [i for i in num if i %2 ==0 and num.index(i)%2==0]

num = [0,1,2,3,4,5,6,7,8,9,10]
result = num_list(num)
print(result)

使用单一的列表生成式来产生一个新的列表

list_data = [1,2,5,8,10,3,18,6,20]
res = [x for x in list_data[::2] if x %2 ==0]
print(res)

用一行代码生成[1,4,9,16,25,36,49,64,81,100]

[x * x for x in range(1,11)]

两个有序列表,l1,l2,对这两个列表进行合并不可使用extend

def loop_merge_sort(l1,l2):
	tmp = []
	while len(l1)>0 and len(l2)>0:
		if l1[0] <l2[0]:
			tmp.append(l1[0])
			del l1[0]
		else:
			tmp.append(l2[0])
			del l2[0]
			
	while len(l1)>0:
		tmp.append(l1[0])
		del l1[0]

	while len(l2)>0:
		tmp.append(l2[0])
		del l2[0]

return tmp

给定一个任意长度数组,实现一个函数

让所有奇数都在偶数前面,而且奇数升序排列,偶数降序排序,如字符串’1982376455’,变
成’1355798642’

# 方法一
def func1(l):
	if isinstance(l, str):
		l = [int(i) for i in l]
		l.sort(reverse=True)

	for i in range(len(l)):
		if l[i] % 2 > 0:
			l.insert(0, l.pop(i))
	print(''.join(str(e) for e in l))


# 方法二
def func2(l):
	print("".join(sorted(l, key=lambda x: int(x) % 2 == 0 and 20 - int(x) or int(x))))

写一个函数找出一个整数数组中,第二大的数

def find_second_large_num(num_list):
	"""
	找出数组第2大的数字
	"""
	# 方法一
	# 直接排序,输出倒数第二个数即可
	tmp_list = sorted(num_list)
	print("方法一\nSecond_large_num is :", tmp_list[-2])
	
	# 方法二
	# 设置两个标志位一个存储最大数一个存储次大数
	# two 存储次大值,one 存储最大值,遍历一次数组即可,先判断是否大于 one,若大于将 one 的值给 two,将 num_list[i] 的值给 one,否则比较是否大于two,若大于直接将 num_list[i] 的值给two,否则pass
	one = num_list[0]
	two = num_list[0]
	for i in range(1, len(num_list)):
		if num_list[i] > one:
			two = one
			one = num_list[i]
		elif num_list[i] > two:
			two = num_list[i]
		print("方法二\nSecond_large_num is :", two)

	# 方法三
	# 用 reduce 与逻辑符号 (and, or)# 基本思路与方法二一样,但是不需要用 if 进行判断。
	from functools import reduce
	num = reduce(lambda ot, x: ot[1] < x and (ot[1], x) or ot[0] < x and (x, ot[1]) or ot, num_list, (0, 0))[0]
	print("方法三\nSecond_large_num is :", num)

if __name__ == '__main___':
	num_list = [34, 11, 23, 56, 78, 0, 9, 12, 3, 7, 5]
	find_second_large_num(num_list)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值