python数据挖掘学习笔记
python数据挖掘学习笔记
python基础知识;
2.python爬虫技术;
3.python数据分析与数据挖掘。
1.python基础
1.输出 print()
2.注释
1.单行注释#
2.多行注释’’’
3.变量
例:a = 5
python中由于没有显示的规定死a的变量类型
所以在a=5之后,
再a = “hello world”
同样可以正确运行
数据类型
数、字符串、列表(list、元祖(tuple、集合(set、字典(dictionary
1.列表[]:abc=["you","me"]
abc->列表abc里面的全部内容;
abc[0]->you
同时,列表里面的数据类型可以多种多样的组合
如abc = ["you",11,14.22]
同时列表里面的内容支持修改。
2.元祖():cde = ("you",12,111.1)
访问的时候同样用[]:cde[1]
元祖里面的内容不支持修改,常量!!!final
3.集合
可以联想数学中的集合,一个数字最多只会保存一次。
例子: >>> a = "adfagfjagpijg"
>>> b = "dfjaifjierjjo"
>>> sa = set(a)
>>> sb = set(b)
>>> sa
{'p', 'i', 'g', 'd', 'j', 'a', 'f'}
>>> sb
{'o', 'i', 'r', 'd', 'j', 'e', 'a', 'f'}
>>> sa&sb
{'i', 'd', 'j', 'a', 'f'}
>>>
&求交集,|求并集。
4.集合{}
内部存储是键值对key-value
{key1:value1,key2:vlaue2}
>>> a = {"11":11,"name":"hh"}
>>> a
{'11': 11, 'name': 'hh'}
>>> a["name"]
'hh'
>>>
注意:python是一门强制缩进的语言
分支:if():
elif():
else:
循环:while():
for i in a:
print(i)
range(0,10):从0开始到9,左闭右开。
中断结构:
break,continue
print(i,end=""):控制不换行
5.函数def func():
6.模块:
多种功能函数组合在一起,形成模块。
系统中自带的模块在安装目录的lib目录中。
导入模块
>>> import urllib
>>> from urllib.request import urlopen
>>> data1=urllib.request.urlopen("http://www.baidu.com").read()
>>> print(data1)
>>> print(len(data1))
153494
>>>
总结,也就是每次定位,需要定位到具体的一个py文件
或者py文件中的具体函数
只定位到urllib是不行的。
7.文件的操作。
python进行文件的打开、关闭、读取、写入
python可以合并多个Excel表的内容
通过句柄来控制。
1.打开文件:open(“文件路径”,"文件操作方式"):文件的创建并打开。
fh=open("D:/pytest/file1.txt","w")
2.文件的写入和关闭
>>> fh=open("D:/pytest/file1.txt","w")
>>> context="我是文件的内容"
>>> fh.write(context)
7
>>> fh.close()
3.文件的读取:
>>> fh=open("D:/pytest/file1.txt","r")
>>> data=fh.read()
>>> print(data)
asdfjfojajfjaffdsafdsafdaskfdsaf
>>> line=fh.readline()
>>> print(line)
>>> fh.read()
''
>>> print(data)
asdfjfojajfjaffdsafdsafdaskfdsaf
8.python的异常处理
python程序在执行的时候,经常会遇到异常,
如果中间异常不处理,经常会导致程序崩溃。
比如后面我们写爬虫的时候,如果不进行异常处理,
很可能虫怕了一半,直接崩溃了。
异常处理实战
try:
...
except Exception as err:
print(err)
比如在for循环中,某一次循环出现异常,
可以在except块中,设置i+1,直接开始下一轮操作。
这是一种处理异常的操作。
而且有了异常处理部分,即使程序出现异常
程序也不会崩溃,而是继续运行下去。
!!!调试:
可以自己采用二分法设置断点,快速找bug
网络爬虫即是一种能够按照特定方向或任意方向自动获取数据的软件工具。
其中较为常见的包括通用型网络爬虫以及专注于特定领域或主题的聚焦型网络爬饼。
爬虫常用于以下场景:
- 网络搜索引擎及其相关内容抓取;
- 收集金融市场相关数据;
- 获取商品销售信息以便进行数据分析;
- 自动识别并剔除网页中的广告信息;
- 收集竞争对手市场动态以便制定运营策略;
- 提取行业资讯数据用于趋势预测分析。
正则表达式:
正则表达式是进行数据筛选的一种表达式。
原子:
原子是正则表达式中最基本的组成单位,
每个正则表达式中至少要有一个原子。
常见的原子类型:
1.普通字符作为原子;
>>> import re
>>> pat="yue"
>>> string="http://yum.iqianyue.com"
>>> rst1=re.search(pat,string)
>>> print(rst1)
<re.Match object; span=(16, 19), match='yue'>
>>> str2="1112121"
>>> rst2=re.search(pat,str2)
>>> print(rst2)
None
2.非打印字符;
\n,\t
3.通用字符;
4.原子表;
在正则表达式中,在明确指定某个位置时即为精准对应。其中\d表示用于匹配数值符号的模式单元格,在单元格中输入\d即可实现与该单元格所包含的数值符号的精准对应;而\w则用于匹配英文字母或数值符号。
‘00\d’可以匹配’007’,但无法匹配’00A’;
‘\d\d\d’可以匹配’010’;
‘\w\w\d’可以匹配’py3’;
.可以匹配任意字符,所以:
'py.‘可以匹配’pyc’、‘pyo’、'py!'等等。
在正则表达式中
来看一个复杂的例子:\d{3}\s+\d{3,8}。
我们来从左到右解读一下:
\d{3}表示匹配3个数字,例如’010’;
\s意味着可以匹配任意空白符(包括空格和Tab),因此\s+表示至少包含一个空白符的模式匹配项;例如匹配' ', ' '等;
\d{3,8}表示3-8个数字,例如’1234567’。
总体而言,该正则表达式能够正确识别和解析带区号且由任意数量空格分隔开的电话号码。
如何用带有连接符的号码进行匹配?连接符在正则表达式中具有特殊含义,在这种情况下需要进行转义处理。因此,在这种情况下使用的正则是\d{3}-\d{3,8}。
但是,仍然无法匹配’010 - 12345’,因为带有空格。所以我们需要更复杂的匹配方式
进阶
要做更精确地匹配,可以用[]表示范围,比如:
[0-9a-zA-Z_]可以匹配一个数字、字母或者下划线;
[0-9a-zA-Z_]+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如’a100’,‘0_Z’,'Py3000’等等;
遵循以下正则表达式模式[a-zA-Z_][0-9a-zA-Z_]*,该模式遵循以下规则:以字母或下划线开头,接着可跟任意数量的数字、字母或下划线。即等同于有效的Python变量名。
[a-zA-Z_][0-9a-zA-Z_]{0, 19}精确地规定了变量长度介于\textit{}至\textit{}\textsub World of Code~\textsub World of Code~\textsub World of Code~\textsub World of Code~\textsub World of Code~\textsub World of Code~\textsub World of Code~\textsub World of Code~\textsub World of Code~-}之间(其中第一个字母后跟不超过 nineteen 位后续数字或其他允许符号)。
A|B可以匹配A或B,所以(P|p)ython可以匹配’Python’或者’python’。
表示行的开头,\d表示必须以数字开头。
在KaTeX中出现解析错误时,在第8个字符的位置上预期应为行末符却得到了一个数字字符\̲d̲(表示行末),该符号仅能以数字形式结尾。
或许你会注意到,在Python中使用'py'时可能会出现一些特定的匹配情况。然而,在某些情况下使用'^py$'会导致其仅对单个字符进行精确匹配而不是整个单词.
re模块
基于已有的基础知识储备,在Python编程语言中应用正则表达式变得十分便捷。
Python内置的re模块集成了全面的正则表达式功能。
注意到在处理特定字符时需特别关注其特殊含义,在运行代码前应确保所有特殊字符都已正确转义以避免潜在的问题。
变量s被赋值为Python中的字符串对象...(其中包含编码信息),其对应的一组字符序列'ABC-001'会被正确解析。鉴于此,在编写正则表达式模式串时'ABC-001'需要特别注意编码部分以避免潜在的转换问题。为了避免这些复杂性'ABC-001'我们建议在Python中使用r前缀来表示正则表达式模式串,则无需处理转义字符。
变量s被赋值为字符串‘ABC-001’(此处"r"可理解为"repr"表示 repr对象),其对应的正则表达式保持不变即其形式为‘ABC-001’。接下来探讨如何判断该正则表达式是否匹配。
先看看如何判断正则表达式是否匹配:
import re
re.match(r’^\d{3}-\d{3,8}$’, ‘010-12345’)
<_sre.SRE_Match object; span=(0, 9), match=‘010-12345’>
re.match(r’^\d{3}-\d{3,8}$’, ‘010 12345’)
该函数用于检测字符串是否与给定模式匹配,并根据结果返回相应的对象或无值。通常采用的方法包括:通过正则表达式来实现模式匹配功能
test = '输入的字符串'
if re.match(r'正则式', test):
打印成功提示
else:
打印失败提示
基于正则表达式的字符串分割
‘a b c’.split(’ ')
[‘a’, ‘b’, ‘’, ‘’, ‘c’]
嗯,无法识别连续的空格,用正则表达式试试:
re.split(r’\s+’, ‘a b c’)
[‘a’, ‘b’, ‘c’]
无论多少个空格都可以正常分割。加入,试试:
re.split(r’[\s,]+’, ‘a,b, c d’)
[‘a’, ‘b’, ‘c’, ‘d’]
再加入;试试:
如果用户提供了一组标签,请记住使用正则表达式将不规范的输入转换为正确的数组形式。
分组
除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组(Group)。比如:
将它们划分为两组,并且能够从匹配到的字符串中获取区号以及本地号码
m = re.match(r’^(\d{3})-(\d{3,8})$’, ‘010-12345’)
m
<_sre.SRE_Match object; span=(0, 9), match=‘010-12345’>
m.group(0)
‘010-12345’
m.group(1)
‘010’
m.group(2)
当正则表达式中定义了分组时,在Match对象上可以通过group()方法提取出相应的子串。
注意到group(0)永远是原始字符串,group(1)、group(2)……表示第1、2、……个子串。
提取子串非常有用。来看一个更凶残的例子:
t = ‘19:05:30’
m = re.match(r’^(0[0-9]|1[0-9]|2[0-3]|[0-9]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9]):(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$’, t)
m.groups()
(‘19’, ‘05’, ‘30’)
这个正则表达式可以直接识别合法的时间。但是有些时候,用正则表达式也无法做到完全验证,比如识别日期:
对于无效日期如2-30、4-31等,“用正则表达式是否能识别这些情况?或者难以直接用正则表达式来处理这些情况?此时往往需要借助程序来辅助完成这一任务。”
贪婪匹配
特别需要注意的是,在正则表达式中,默认采用贪婪策略,并非非贪婪策略。即尽可能多地匹配字符。如举例所示,在数字后紧跟的零将被成功识别。
re.match(r'(^(\d+) (.*))$', '123456-789').groups()
('123456', '789')
因为正则表达式的分组模式采用了贪婪算法,在处理数字串时会优先吃掉所有可用的数字字符。
.groups()的结果为('123456', '789')
为了最小化地进行\d+的配对(即尽可能地减少其长度),从而能够识别出后面的以0结尾的部分;通过添加一个问号即可实现这一点。
re.match(r’^(\d+?)(0*)$’, ‘102300’).groups()
(‘1023’, ‘00’)
小结
正则表达式具有强大的功能,在有限篇幅内无法实现全面讲解。阐述所有细节将耗尽篇幅,并非易于掌握的内容。在实际应用中遇到相关问题时,请参考专门资料以解决这类问题通常需要依赖专业的参考资料和实践积累。
在运行过程中随时可更改变量,在具体操作中可将name字段设置为'Bill';若未将修改后的值(如Bill)保存至磁盘上,则下次重新运行程序时,默认状态会将其恢复为初始值Bob。
我们把变量从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flattening等等,都是一个意思。
序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。
反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling。
JSON
如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。
JSON表示的对象就是标准的JavaScript语言的对象,JSON和Python内置的数据类型对应如下:
JSON类型 Python类型
{} dict
[] list
"string" str
1234.56 int或float
true/false True/False
null None
Python内置的json模块提供了非常完善的Python对象到JSON格式的转换。我们先看看如何把Python对象变成一个JSON:
>>> import json
>>> d = dict(name='Bob', age=20, score=88)
>>> json.dumps(d)
'{"age": 20, "score": 88, "name": "Bob"}'
dumps()方法返回一个str,内容就是标准的JSON。类似的,dump()方法可以直接把JSON写入一个file-like Object。
要把JSON反序列化为Python对象,用loads()或者对应的load()方法,前者把JSON的字符串反序列化,后者从file-like Object中读取字符串并反序列化:
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'
>>> json.loads(json_str)
{'age': 20, 'score': 88, 'name': 'Bob'}
由于JSON标准规定JSON编码是UTF-8,所以我们总是能正确地在Python的str与JSON的字符串之间转换。
Unix/Linux 操作系统引入了 fork() 系统调用这一机制。在多线程编程中,默认采用单线程模式可能显得效率低下;而使用 fork() 机制时会生成两个独立的任务:父进程与子进程。每个父进程都能独立执行自己的任务序列而不互相干扰。
子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。
有了fork调用,一个进程在接到新任务时就可以复制出一个子进程来处理新任务,常见的Apache服务器就是由父进程监听端口,每当有新的http请求时,就fork出子进程来处理新的http请求。
multiprocessing:
如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择。由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序?
由于Python是跨平台的,自然也应该提供一个跨平台的多进程支持。multiprocessing模块就是跨平台版本的多进程模块。
multiprocessing模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:
from multiprocessing import Process
import os
# 子进程要执行的代码
def run_proc(name):
print('Run child process %s (%s)...' % (name, os.getpid()))
if __name__=='__main__':
print('Parent process %s.' % os.getpid())
p = Process(target=run_proc, args=('test',))
print('Child process will start.')
p.start()
p.join()
print('Child process end.')
执行结果如下:
Parent process 928.
Process will start.
Run child process test (929)...
Process end.
创建子进程时,只需要传入一个执行函数和函数的参数,创建一个Process实例,用start()方法启动,这样创建进程比fork()还要简单。
join()方法可以等待子进程结束后再继续往下运行,通常用于进程间的同步。
Pool:
如果要启动大量的子进程,可以用进程池的方式批量创建子进程:
对Pool对象调用join()方法会等待所有子进程执行完毕,调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了。
请注意输出的结果,task 0,1,2,3是立刻执行的,而task 4要等待前面某个task完成后才执行,这是因为Pool的默认大小在我的电脑上是4,因此,最多同时执行4个进程。这是Pool有意设计的限制,并不是操作系统的限制。如果改成:
p = Pool(5)
就可以同时跑5个进程。
由于Pool的默认大小是CPU的核数,如果你不幸拥有8核CPU,你要提交至少9个子进程才能看到上面的等待效果。
进程间通信
Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
我们以Queue为例,在父进程中创建两个子进程,一个往Queue里写数据,一个从Queue里读数据:
Lock
多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。
当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。
获得锁的线程用完后一定要释放锁,否则那些苦苦等待锁的线程将永远等待下去,成为死线程。所以我们用try...finally来确保锁一定会被释放。
锁的好处就是确保了某段关键代码只能由一个线程从头到尾完整地执行,坏处当然也很多,首先是阻止了多线程并发执行,包含锁的某段代码实际上只能以单线程模式执行,效率就大大地下降了。其次,由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束,只能靠操作系统强制终止。
小结
多线程编程,模型复杂,容易发生冲突,必须用锁加以隔离,同时,又要小心死锁的发生。
Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中就是一个美丽的梦。
ThreadLocal
在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁。
但是局部变量也有问题,就是在函数调用的时候,传递起来很麻烦
def process_student(name):
std = Student(name)
# std是局部变量,但是每个函数都要用它,因此必须传进去:
do_task_1(std)
do_task_2(std)
def do_task_1(std):
do_subtask_1(std)
do_subtask_2(std)
def do_task_2(std):
do_subtask_2(std)
do_subtask_2(std)
每个函数一层一层调用都这么传参数那还得了?用全局变量?也不行,因为每个线程处理不同的Student对象,不能共享。
ThreadLocal应运而生,不用查找dict,ThreadLocal帮你自动做这件事
import threading
# 创建全局ThreadLocal对象:
local_school = threading.local()
def process_student():
# 获取当前线程关联的student:
std = local_school.student
print('Hello, %s (in %s)' % (std, threading.current_thread().name))
def process_thread(name):
# 绑定ThreadLocal的student:
local_school.student = name
process_student()
t1 = threading.Thread(target= process_thread, args=('Alice',), name='Thread-A')
t2 = threading.Thread(target= process_thread, args=('Bob',), name='Thread-B')
t1.start()
t2.start()
t1.join()
t2.join()
执行结果:
Hello, Alice (in Thread-A)
Hello, Bob (in Thread-B)
全局变量local_school就是一个ThreadLocal对象,每个Thread对它都可以读写student属性,但互不影响。你可以把local_school看成全局变量,但每个属性如local_school.student都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理。
可以理解为全局变量local_school是一个dict,不但可以用local_school.student,还可以绑定其他变量,如local_school.teacher等等。
ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
小结
一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题。
进程 vs. 线程
阅读: 84741
我们介绍了多进程和多线程,这是实现多任务最常用的两种方式。现在,我们来讨论一下这两种方式的优缺点。
首先,要实现多任务,通常我们会设计Master-Worker模式,Master负责分配任务,Worker负责执行任务,因此,多任务环境下,通常是一个Master,多个Worker。
如果用多进程实现Master-Worker,主进程就是Master,其他进程就是Worker。
如果用多线程实现Master-Worker,主线程就是Master,其他线程就是Worker。
多进程模式最大的优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程。(当然主进程挂了所有进程就全挂了,但是Master进程只负责分配任务,挂掉的概率低)著名的Apache最早就是采用多进程模式。
多进程模式的缺点是创建进程的代价大,在Unix/Linux系统下,用fork调用还行,在Windows下创建进程开销巨大。另外,操作系统能同时运行的进程数也是有限的,在内存和CPU的限制下,如果有几千个进程同时运行,操作系统连调度都会成问题。
多线程模式通常比多进程快一点,但是也快不到哪去,而且,多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存。在Windows上,如果一个线程执行的代码出了问题,你经常可以看到这样的提示:“该程序执行了非法操作,即将关闭”,其实往往是某个线程出了问题,但是操作系统会强制结束整个进程。
在Windows下,多线程的效率比多进程要高,所以微软的IIS服务器默认采用多线程模式。由于多线程存在稳定性的问题,IIS的稳定性就不如Apache。为了缓解这个问题,IIS和Apache现在又有多进程+多线程的混合模式,真是把问题越搞越复杂。
线程切换
无论是多进程还是多线程,只要数量一多,效率肯定上不去,为什么呢?
我们打个比方,假设你不幸正在准备中考,每天晚上需要做语文、数学、英语、物理、化学这5科的作业,每项作业耗时1小时。
如果你先花1小时做语文作业,做完了,再花1小时做数学作业,这样,依次全部做完,一共花5小时,这种方式称为单任务模型,或者批处理任务模型。
假设你打算切换到多任务模型,可以先做1分钟语文,再切换到数学作业,做1分钟,再切换到英语,以此类推,只要切换速度足够快,这种方式就和单核CPU执行多任务是一样的了,以幼儿园小朋友的眼光来看,你就正在同时写5科作业。
但是,切换作业是有代价的,比如从语文切到数学,要先收拾桌子上的语文书本、钢笔(这叫保存现场),然后,打开数学课本、找出圆规直尺(这叫准备新环境),才能开始做数学作业。操作系统在切换进程或者线程时也是一样的,它需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后,把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行。这个切换过程虽然很快,但是也需要耗费时间。如果有几千个任务同时进行,操作系统可能就主要忙着切换任务,根本没有多少时间去执行任务了,这种情况最常见的就是硬盘狂响,点窗口无反应,系统处于假死状态。
所以,多任务一旦多到一个限度,就会消耗掉系统所有的资源,结果效率急剧下降,所有任务都做不好。
计算密集型 vs. IO密集型
是否采用多任务的第二个考虑是任务的类型。我们可以把任务分为计算密集型和IO密集型。
计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。
计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。
第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。
IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。
