首页 文章 精选 留言 我的

精选列表

搜索[快速],共10000篇文章
优秀的个人博客,低调大师

docker学习系列1 使用docker 快速实现多版本PHP

多谢此文:https://blog.eriksen.com.br/en/docker-image-multi-version-php-development 最近一个新的后台API项目需要运行在PHP5.3环境中,而无论是本地还是测试服务器都安装的是PHP7.x PHP5.3官方已经不维护了,通过源码安装配置也很麻烦,我又不想污染了现有的环境。 所以想到了docker 我觉得docker适合以下情况: 运行特定的开发环境,比如要运行两个项目。一个要求PHP5.6,一个PHP7.0。不想来回切换。 喜欢尝鲜,折腾,docker有很强的隔离性。在docker里搞坏也不会破坏本地 新项目是基于 ThinkPHP3.2 想通过docker跑起来,可以按如下步骤: 安装 docker,略 记得一定要切换为国内源,不然速度巨慢,还容易报错,推荐免费的https://www.daocloud.io/mirror#accelerator-doc 下载镜像docker pull eriksencosta/php-dev 项目目录是已经存在的 路径是 D:/projects/live-ranking-api 运行容器 其中参数:-p 端口映射 -v 挂载目录,冒号前是宿主机目录,后面的是容器内目录 -t -i 参数 表示已交互方式运行容器,运行成功后会执行 /bin/bash 就是进去终端docker run -t -i -p 8088:80 -v D:/projects/live-ranking-api:/var/www -d "eriksencosta/php-dev:latest" /bin/bash image.png 打开浏览器输入 localhost:8088 正常的话项目已经成功跑起来了 切换PHP版本,在容器内的终端内输入 phpenv命令 列出当前可选择的PHP版本 # phpenv versions 5.3 5.3.29 5.4 5.4.35 5.5 5.5.19 5.6 * 5.6.3 (set by /opt/phpenv/version) 执行 phpenv global 5.4 # phpenv global 5.4 # php -v PHP 5.4.35 (cli) (built: Dec 14 2014 00:35:12) Copyright (c) 1997-2014 The PHP Group Zend Engine v2.4.0, Copyright (c) 1998-2014 Zend Technologies with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies with Xdebug v2.2.6, Copyright (c) 2002-2014, by Derick Rethans 启动nginx # webserver start Starting PHP-FPM (PHP version 5.3) server. Starting Nginx server. Done. 参考:https://hub.docker.com/r/eriksencosta/php-dev/https://github.com/eriksencosta/silex-docker-example

优秀的个人博客,低调大师

Python:用numpy+OpenCV快速实现矫正图像的功能

透视变换是一个很实用的功能,当用手机去拍证件或者名片时,经常会拍歪,或者有边框。如果你使用过类似“扫描全能王”的软件,你应该知道,他们会自动把证件矫正并除边框,它就是通过透视变换实现的,和numpy中的仿射变换一样。 左图为原图,右图为矫正后的图 1.运行环境 Python3.6.5 pycharm win10 安装oponcv, numpy 库安装教程链接 2.代码 import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread('E:\\card2.jpg') rows, cols = img.shape[:2] # 原图中书本的四个角点 pts1 = np.float32([[69, 163], [704, 62], [162, 675], [970, 411]]) # 变换后分别在左上、右上、左下、右下四个点 pts2 = np.float32([[0, 1000], [0, 0], [750, 1000], [750, 0]]) # 生成透视变换矩阵 M = cv2.getPerspectiveTransform(pts1, pts2) # 进行透视变换 dst = cv2.warpPerspective(img, M, (750, 1000)) plt.subplot(121), plt.imshow(img[:, :, ::-1]), plt.title('input') plt.subplot(122), plt.imshow(dst[:, :, ::-1]), plt.title('output') # img[:, :, ::-1]是将BGR转化为RGB plt.show() 3.解析 图中角点坐标需要自己设定 通过锁定书的角点,并设定改变后的角点,来达到矫正图像的目的。 坐标原点在图片左上角!

优秀的个人博客,低调大师

Java多线程 -- 互斥锁/共享锁/读写锁 快速入门

什么是互斥锁? 在访问共享资源之前对进行加锁操作,在访问完成之后进行解锁操作。 加锁后,任何其他试图再次加锁的线程会被阻塞,直到当前进程解锁。 如果解锁时有一个以上的线程阻塞,那么所有该锁上的线程都被编程就绪状态, 第一个变为就绪状态的线程又执行加锁操作,那么其他的线程又会进入等待。 在这种方式下,只有一个线程能够访问被互斥锁保护的资源。 什么是共享锁? 互斥锁要求只能有一个线程访问被保护的资源,共享锁从字面来看也即是允许多个线程共同访问资源。 什么是读写锁? 读写锁既是互斥锁,又是共享锁,read模式是共享,write是互斥(排它锁)的。 读写锁有三种状态:读加锁状态、写加锁状态和不加锁状态 一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。 用ReentrantLock手动实现一个简单的读写锁。 MyReadWriteLock.java /** * Created by Fant.J. */ public class MyReadWriteLock { private Map<String,Object> map = new HashMap<>(); private ReadWriteLock rwl = new ReentrantReadWriteLock(); private Lock r = rwl.readLock(); private Lock w = rwl.writeLock(); public Object get(String key){ try { r.lock(); System.out.println(Thread.currentThread().getName()+"read 操作执行"); Thread.sleep(500); return map.get(key); } catch (InterruptedException e) { e.printStackTrace(); return null; } finally { r.unlock(); System.out.println(Thread.currentThread().getName()+"read 操作结束"); } } public void put(String key,Object value){ try { w.lock(); System.out.println(Thread.currentThread().getName()+"write 操作执行"); Thread.sleep(500); map.put(key,value); } catch (InterruptedException e) { e.printStackTrace(); } finally { w.unlock(); System.out.println(Thread.currentThread().getName()+"write 操作结束"); } } } 测试读读共享(不互斥) /** * Created by Fant.J. */ public class Test { public static void main(String[] args) { MyReadWriteLock myReadWriteLock = new MyReadWriteLock(); myReadWriteLock.put("a","fantj_a"); //读读不互斥(共享) //读写互斥 new Thread(new Runnable() { @Override public void run() { System.out.println(myReadWriteLock.get("a")); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println(myReadWriteLock.get("a")); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println(myReadWriteLock.get("a")); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println(myReadWriteLock.get("a")); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println(myReadWriteLock.get("a")); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println(myReadWriteLock.get("a")); } }).start(); } } mainwrite 操作执行 mainwrite 操作结束 Thread-1read 操作执行 Thread-2read 操作执行 Thread-0read 操作执行 Thread-3read 操作执行 Thread-4read 操作执行 Thread-5read 操作执行 Thread-1read 操作结束 Thread-0read 操作结束 Thread-2read 操作结束 Thread-3read 操作结束 fantj_a fantj_a fantj_a fantj_a Thread-4read 操作结束 fantj_a Thread-5read 操作结束 fantj_a 可以看出,中间有很多read操作是并发进行的。 那么我们再看下写写是否有互斥性: /** * 测试 写-写 模式 是互斥的 * Created by Fant.J. */ public class TestWriteWrite { public static void main(String[] args) { MyReadWriteLock myReadWriteLock = new MyReadWriteLock(); new Thread(new Runnable() { @Override public void run() { myReadWriteLock.put("b","fantj_b"); } }).start(); new Thread(new Runnable() { @Override public void run() { myReadWriteLock.put("b","fantj_b"); } }).start(); new Thread(new Runnable() { @Override public void run() { myReadWriteLock.put("b","fantj_b"); } }).start(); new Thread(new Runnable() { @Override public void run() { myReadWriteLock.put("b","fantj_b"); } }).start(); new Thread(new Runnable() { @Override public void run() { myReadWriteLock.put("b","fantj_b"); } }).start(); } } Thread-0write 操作执行 Thread-1write 操作执行 Thread-0write 操作结束 Thread-1write 操作结束 Thread-2write 操作执行 Thread-2write 操作结束 Thread-3write 操作执行 Thread-3write 操作结束 Thread-4write 操作执行 Thread-4write 操作结束 控制台能明显感觉到线程休息的时间。所以它的写-写操作肯定是互斥的。 最后再看看,写-读 操作是否互斥。 写-读互斥 测试 /** * 测试 写-读模式 互斥 * Created by Fant.J. */ public class TestWriteRead { public static void main(String[] args) { MyReadWriteLock myReadWriteLock = new MyReadWriteLock(); //读读不互斥(共享) //读写互斥 new Thread(new Runnable() { @Override public void run() { myReadWriteLock.put("a","fantj_a"); } }).start(); new Thread(new Runnable() { @Override public void run() { System.out.println(myReadWriteLock.get("a")); } }).start(); } } Thread-0write 操作执行 Thread-1read 操作执行 Thread-0write 操作结束 Thread-1read 操作结束 fantj_a 控制台可以看到write操作执行后线程被阻塞。直到写释放了锁。 问题分析 问题1:仔细想了想,如果有一种场景,就是用户一直再读,写获取不到锁,那么不就造成脏读吗? 上一章我介绍了公平锁/非公平锁,资源的抢占不就是非公平锁造成的吗,那我们用公平锁把它包装下不就能避免了吗,我做了个简单的实现:(不知道公平锁的可以翻我上章教程) /** * 测试 读写锁 的公平锁 实现 * Created by Fant.J. */ public class TestReadWriteRead { public static void main(String[] args) { ReentrantLock fairLock = new ReentrantLock(true); MyReadWriteLock myReadWriteLock = new MyReadWriteLock(); myReadWriteLock.put("a","fantj_a"); new Thread(new Runnable() { @Override public void run() { fairLock.lock(); System.out.println(myReadWriteLock.get("a")); System.out.println("fair队列长度"+fairLock.getQueueLength()); fairLock.unlock(); } }).start(); new Thread(new Runnable() { @Override public void run() { fairLock.lock(); System.out.println(myReadWriteLock.get("a")); System.out.println("fair队列长度"+fairLock.getQueueLength()); fairLock.unlock(); } }).start(); new Thread(new Runnable() { @Override public void run() { fairLock.lock(); System.out.println(myReadWriteLock.get("a")); System.out.println("fair队列长度"+fairLock.getQueueLength()); fairLock.unlock(); } }).start(); new Thread(new Runnable() { @Override public void run() { fairLock.lock(); myReadWriteLock.put("a","fantj_a_update"); System.out.println("fair队列长度"+fairLock.getQueueLength()); fairLock.unlock(); } }).start(); new Thread(new Runnable() { @Override public void run() { fairLock.lock(); System.out.println(myReadWriteLock.get("a")); System.out.println("fair队列长度"+fairLock.getQueueLength()); fairLock.unlock(); } }).start(); } } mainwrite 操作执行 mainwrite 操作结束 Thread-0read 操作执行 Thread-0read 操作结束 fantj_a fair队列长度4 Thread-1read 操作执行 Thread-1read 操作结束 fantj_a fair队列长度3 Thread-2read 操作执行 Thread-2read 操作结束 fantj_a fair队列长度2 Thread-3write 操作执行 Thread-3write 操作结束 fair队列长度1 Thread-4read 操作执行 Thread-4read 操作结束 fantj_a_update fair队列长度0 如果谁有更好的实现方式(或者java有现成的实现工具类/包),可在评论区留言,我在百度上没有找到读写锁的公平锁实现~~谢谢!

优秀的个人博客,低调大师

如何快速成为数据分析师(个人角度)

说来我正式接触数据分析也快一年,对速成还是有一些心得。优秀的数据分析师是不能速成的,但是零经验也有零经验的捷径。 分享之前我还是要推荐下我自己创建的大数据学习资料分享群 710219868 代号风火,这是全国最大的大数据学习交流的地方,2000人聚集,不管你是小白还是大牛,小编我都挺欢迎,今天的已经资讯上传到群文件,不定期分享干货,包括我自己整理的一份最新的适合2018年学习的大数据教程,欢迎初学和进阶中的小伙伴。 以上的前提针对入门,目的是达到数据分析师的门槛,顺利拿到一份offer,不涉及数据挖掘等高级技巧。我的方法倾向互联网领域,不论是分析师这个职位,还是运营、产品的能力发展都是适用的。其他领域就仁者见仁了。 市面上有《七周七数据库》,《七周七编程语言》。今天我们就《七周七学习成为数据分析师》。 没错,七周。 第一周:Excel学习掌握 如果Excel玩的顺溜,你可以略过这一周。不过介于我入行时也不会vlookup,所以有必要讲下。 重点是了解各种函数,包括但不限于sum,count,sumif,countif,find,if,left/right,时间转换等。 Excel函数不需要学全,重要的是学会搜索。即如何将遇到的问题在搜索引擎上描述清楚。 我认为掌握vlookup和数据透视表足够,是最具性价比的两个技巧。学会vlookup,SQL中的join,Python中的merge很容易理解。学会数据透视表,SQL中的group,Python中的pivot_table也是同理。 这两个搞定,基本10万条以内的数据统计没啥难度,80%的办公室白领都能秒杀。 Excel是熟能生巧,多找练习题。还有需要养成好习惯,不要合并单元格,不要过于花哨。表格按照原始数据(sheet1)、加工数据(sheet2),图表(sheet3)的类型管理。 专栏上写了三篇Excel的文章,比较简单,大体介绍了Excel应用,可以作为职场新人的指南。 第一篇数据分析—函数篇。主要简单讲解常用的函数,以及与之对应的SQL/Python函数。 第二篇数据分析—技巧篇。主要简单讲解我认为很有新价比的功能,提高工作效率。 第三篇数据分析—实战篇。主要将前两篇的内容以实战方式进行,简单地进行了一次数据分析。数据源采用了真实的爬虫数据,是5000行数据分析师岗位数据。 下面是为了以后更好的基础而附加的学习任务。 了解单元格格式,后期的数据类型包括各类timestamp,date,string,int,bigint,char,factor,float等。 了解数组,以及怎么用(excel的数组挺难用),Python和R也会涉及到 list。 了解函数和参数,当进阶为编程型的数据分析师时,会让你更快的掌握。 了解中文编码,UTF8和ASCII,包括CSV的delimiter等,以后你会回来感谢我的。 养成一个好习惯,不要合并单元格,不要过于花哨。表格按照原始数据、加工数据,图表的类型管理 第二周:数据可视化 数据分析界有一句经典名言,字不如表,表不如图。数据可视化是数据分析的主要方向之一。除掉数据挖掘这类高级分析,不少数据分析就是监控数据观察数据。 数据分析的最终都是要兜售自己的观点和结论的。兜售的最好方式就是做出观点清晰数据详实的PPT给老板看。如果没人认同分析结果,那么分析也不会被改进和优化,不落地的数据分析价值又在哪里? 首先要了解常用的图表: 各类图表的详细介绍可以查看第四篇文章:数据可视化:你想知道的经典图表全在这 了解图表后,还应该学会报表制作,这里准备了第五篇:数据可视化:打造高端的数据报表。将教会大家Excel的高级图表用法。 如果还不过瘾,我们得掌握信息图和BI BI(商业智能)和图表的区别在于BI擅长交互和报表,更擅长解释已经发生和正在发生的数据。将要发生的数据是数据挖掘的方向。 BI的好处在于很大程度解放数据分析师的工作,推动全部门的数据意识,另外降低其他部门的数据需求(万恶的导数据)。 BI市面上的产品很多,基本都是建立仪表盘Dashboard,通过维度的联动和钻取,获得可视化的分析。第六篇:数据可视化:深入浅出BI 将以第一周的实战数据学习BI,上图的就是学习后的成果。 数据可视化的学习就是三个过程,了解数据(图表),整合数据(BI),展示数据(信息化)。 可视化也和审美息息相关,很多直男代表并不擅长做图,没关系,抽空可以看书:数据之美 (豆瓣) PPT也别落下,Excel作图多练习,不会有坏处的

优秀的个人博客,低调大师

Python3快速入门——(7)迭代(iterable)和迭代器

迭代(iterable) #任何可迭代对象都可以作用于for循环,包括我们自定义的数据类型,只要符合迭代条件,就可以使用for循环 d = { 'a' : 1 , 'b' : 2 , 'c' : 3 } #对dict迭代 for k,v in d.items(): # 如果要同时迭代key和value,可以用for k, v in d.items() print (k,v) #默认情况下,dict迭代的是key # 如果要迭代value,可以用for value in d.values() #字符串也是可迭代对象,因此,也可以作用于for循环 #如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断 from collections import Iterable #导入collections模块的Iterable类型 n= isinstance (d,Iterable) print (n) #结果为True,可迭代 #Python内置的 enumerate 函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身 names=[ 'a' , 'b' , 'c' , 'd' ] for i,value in enumerate (names): #for循环同时引用两个变量 print (i,value) for x,y in [( 1 , 2 ),( 3 , 5 ),( 5 , 6 )]: ##for循环同时引用两个变量 print (x,y) 迭代器 可以直接作用于 for 循环的数据类型有以下几种: 一类是 集合数据类型 ,如 list 、 tuple 、 dict 、 set 、 str 等; 一类是 generator ,包括生成器和带 yield 的generator function。 这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable #可以使用isinstance()判断一个对象是否是Iterable对象 from collections import Iterable m= isinstance ([],Iterable) #True #而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值, # 直到最后抛出StopIteration错误表示无法继续返回下一个值了。 #可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator #生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。 #把list、dict、str等Iterable变成Iterator可以使用iter()函数 from collections import Iterator m= isinstance ( iter ( 'abc' ),Iterator) #True 凡是可作用于 for 循环的对象都是 Iterable 类型; 凡是可作用于 next() 函数的对象都是 Iterator 类型,它们表示一个惰性计算的序列; 集合数据类型如 list 、 dict 、 str 等是 Iterable 但不是 Iterator ,不过可以通过 iter() 函数获得一个 Iterator 对象。 Python的 for 循环本质上就是通过不断调用 next() 函数实现的

优秀的个人博客,低调大师

Python进行数据可视化分析快速教程实例

Jupyter Notebook介绍 Jupyter Notebook是一个交互式笔记本,支持运行 40 多种编程语言。IPython notebook 是一个基于 IPython REPL 的 web 应用,安装 IPython 后在终端输入 ipython notebook 即可启动服务。jupyter 是把 IPython 和 Python 解释器剥离后的产物,将逐渐替代 IPython 独立发行。jupyter 可以和 Python 之外的 程序结合,提供新的、强大的服务。比如 Ruby REPL 环境 IRuby 和 Julia REPL 环境 IJulia。相对的,jypyter 也提供 jupyter notebook。 Jupyter Notebook的安装 安装pyzmq,Pyzmq是zeromq的Python绑定。z

优秀的个人博客,低调大师

Python3快速入门——(4)循环结构和判断结构

循环结构 #循环结构 cities=[ "Austin" , "Dallas" , "Houston" ] for city in cities: #for循环 print (city) #通过缩进控制整体结构 i= 0 while i< 3 : #while循环 i+= 1 print (i) for j in range ( 10 ): #range(n)表示从0到n-1的n个数 print (j) cities=[[ "Austin" , "Dallas" , "Houston" ],[ "Haerbin" , "Shanghai" , "Beijing" ]] #list中的元素仍是list for city in cities: print (city) #输出list中的两个list元素 for i in cities: #两层for循环输出两个list中的每个元素 for j in i: print (j) #注意: 如果代码写得有问题,会让程序陷入“死循环”,也就是永远循环下去。这时可以用 Ctrl+C 退出程序,或者强制结束Python进程。 #注意 : Python提供一个 range() 函数,可以生成一个整数序列,再通过 list() 函数可以转换为list #例如: list ( range (5)) 结果为:[0, 1, 2, 3, 4] 判断结构 #选择结构 cat= True #bool类型值 dog= False print ( type (cat)) #<class 'bool'> print ( 8 == 8 ) #True 判断语句 print ( 8 != 8 ) #False print ( 10 >= 5 ) #True sample_rate= 700 if (sample_rate> 50 ): #if语句选择 print (sample_rate) else : print ( 'less lan' )

优秀的个人博客,低调大师

Centos7YUM快速搭建LNMP环境并简易优化

下载并安装NGINX wget http://nginx.org/packages/mainline/centos/7/x86_64/RPMS/nginx-1.13.9-1.el7_4.ngx.x86_64.rpm rpm -ivh nginx-1.13.9-1.el7_4.ngx.x86_64.rpm systemctl enable nginx systemctl start nginx 下载并安装MYSQL wget https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm rpm -ivh mysql57-community-release-el7-11.noarch.rpm yum install -y mysql-server systemctl enable mysqld systemctl start mysqld 下载并安装PHP yum install http://rpms.remirepo.net/enterprise/remi-release-7.rpm yum --enablerepo=remi,remi-php72 install php php-common php-cli php-fpm php-pdo php-bcmath php-mysqlnd php-mbstring php-mcrypt php-gd php-dom php-xml php-zip chmod 777 -R /var/lib/php/session systemctl enable php-fpm systemctl start php-fpm 开启PHP缓存 - 修改php.ini中: realpath_cache_size = 4096k realpath_cache_ttl = 120 开启NGINX的GZIP并隐藏版本号 - 修改nginx.conf中: server_tokens off; gzip on; gzip_min_length 20k; gzip_buffers 4 16k; gzip_comp_level 4; gzip_types text/plain text/css text/javascript application/x-javascript application/xml application/x-httpd-php; server { listen 80 default; return 444; } 说明:优化仅针对低配服务器,是最简易的优化,可明显提升运行效率,配置不高的服务器不要过分优化,否则反而会增加服务器压力。

优秀的个人博客,低调大师

(转)C# 快速高效率复制对象的方式

1、需求 在项目代码中经常需要把对象复制到新的对象中,或者把属性名相同的值复制一遍。 比如: public class Student { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } public class StudentSecond { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } Student s = new Student() { Age = 20, Id = 1, Name = "Emrys" }; 我们需要给新的Student赋值 Student ss = new Student { Age = s.Age, Id = s.Id, Name = s.Name }; 给另一个类StudentSecond的属性赋值,两个类属性的名称和类型一致。 StudentSecond ss = new StudentSecond { Age = s.Age, Id = s.Id, Name = s.Name }; 2、解决办法 当然最原始的办法就是把需要赋值的属性全部手动手写。这样的效率是最高的。但是这样代码的重复率太高,更重要的是浪费时间,如果一个类有几十个属性,那一个一个属性赋值太浪费了,像这样重复的劳动工作更应该是需要优化的。 2.1、反射 反射应该是很多人用过的方法,就是封装一个类,反射获取属性和设置属性的值。 private static TOut TransReflection<TIn, TOut>(TIn tIn) { TOut tOut = Activator.CreateInstance<TOut>(); var tInType = tIn.GetType(); foreach (var itemOut in tOut.GetType().GetProperties()) { var itemIn = tInType.GetProperty(itemOut.Name); ; if (itemIn != null) { itemOut.SetValue(tOut, itemIn.GetValue(tIn)); } } return tOut; } 调用:StudentSecond ss= TransReflection<Student, StudentSecond>(s); 调用一百万次耗时:2464毫秒 2.2、序列化 序列化的方式有很多种,有二进制、xml、json等等,今天我们就用Newtonsoft的json进行测试。 调用:StudentSecondss= JsonConvert.DeserializeObject<StudentSecond>(JsonConvert.SerializeObject(s)); 调用一百万次耗时:2984毫秒 3、表达式树 3.1、简介 关于表达式树不了解的可以百度。 也就是说复制对象也可以用表达式树的方式 Expression<Func<Student, StudentSecond>> ss = (x) => new StudentSecond { Age = x.Age, Id = x.Id, Name = x.Name }; var f = ss.Compile(); StudentSecond studentSecond = f(s); 这样的方式我们可以达到同样的效果。看似说这样的写法和最原始的复制没有什么区别,代码反而变多了呢,然而这个只是第一步。跟着来!!!!!!!!!!!!!!!!!!!!!!! 3.2、分析代码 用ILSpy反编译下这段表达式代码如下: ParameterExpression parameterExpression; Expression<Func<Student, StudentSecond>> ss = Expression.Lambda<Func<Student, StudentSecond>>(Expression.MemberInit(Expression.New(typeof(StudentSecond)), new MemberBinding[] { Expression.Bind(methodof(StudentSecond.set_Age(int)), Expression.Property(parameterExpression, methodof(Student.get_Age()))), Expression.Bind(methodof(StudentSecond.set_Id(int)), Expression.Property(parameterExpression, methodof(Student.get_Id()))), Expression.Bind(methodof(StudentSecond.set_Name(string)), Expression.Property(parameterExpression, methodof(Student.get_Name()))) }), new ParameterExpression[] { parameterExpression }); Func<Student, StudentSecond> f = ss.Compile(); StudentSecond studentSecond = f(s); 只要用反射循环所有的属性然后Expression.Bind所有的属性。最后调用Compile()(s)就可以获取正确的StudentSecond。 到这有的人又要问了,如果用反射的话那岂不是效率很低,和直接用反射或者用序列化没什么区别吗? 当然这个可以解决的,就是我们的表达式树可以缓存。只是第一次用的时候需要反射,以后再用就不需要反射了 3.3、复制对象通用代码 为了通用性所以其中的Student和StudentSecond分别泛型替换。 private static Dictionary<string, object> _Dic = new Dictionary<string, object>(); private static TOut TransExp<TIn, TOut>(TIn tIn) { string key = string.Format("trans_exp_{0}_{1}", typeof(TIn).FullName, typeof(TOut).FullName); if (!_Dic.ContainsKey(key)) { ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p"); List<MemberBinding> memberBindingList = new List<MemberBinding>(); foreach (var item in typeof(TOut).GetProperties()) { if (!item.CanWrite) continue; MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name)); MemberBinding memberBinding = Expression.Bind(item, property); memberBindingList.Add(memberBinding); } MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray()); Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression }); Func<TIn, TOut> func = lambda.Compile(); _Dic[key] = func; } return ((Func<TIn, TOut>)_Dic[key])(tIn); } 调用:StudentSecond ss= TransExp<Student, StudentSecond>(s); 调用一百万次耗时:564毫秒 3.4、利用泛型的特性再次优化代码 不用字典存储缓存,因为泛型就可以很容易解决这个问题。 public static class TransExpV2<TIn, TOut> { private static readonly Func<TIn, TOut> cache = GetFunc(); private static Func<TIn, TOut> GetFunc() { ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p"); List<MemberBinding> memberBindingList = new List<MemberBinding>(); foreach (var item in typeof(TOut).GetProperties()) { if (!item.CanWrite) continue; MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name)); MemberBinding memberBinding = Expression.Bind(item, property); memberBindingList.Add(memberBinding); } MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray()); Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression }); return lambda.Compile(); } public static TOut Trans(TIn tIn) { return cache(tIn); } } 调用:StudentSecond ss= TransExpV2<Student, StudentSecond>.Trans(s); 调用一百万次耗时:107毫秒 耗时远远的小于使用automapper的338毫秒。 4、总结 从以上的测试和分析可以很容易得出,用表达式树是可以达到效率与书写方式二者兼备的方法之一,总之比传统的序列化和反射更加优秀。 最后望对各位有所帮助 借鉴之处:http://www.cnblogs.com/emrys5/

优秀的个人博客,低调大师

一分钟使用Docker快速搭建Wordpress

版权声明:本文可能为博主原创文章,若标明出处可随便转载。 https://blog.csdn.net/Jailman/article/details/79081418 1. apt install docker.io -y 2. pip install docker-compose 3. vimwordpress_stack.yml version: '3.1' services: wordpress: image: wordpress restart: always ports: - 80:80 environment: WORDPRESS_DB_PASSWORD: mysqlrootpasswd mysql: image: mysql:5.7 restart: always environment: MYSQL_ROOT_PASSWORD: mysqlrootpasswd4. vim start.sh #!/bin/bash docker-compose -f wordpress_stack.yml up -d5. ./start.sh 6. iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT 7. 打开http://localhost安装Wordpress

优秀的个人博客,低调大师

win10+py35,两步快速安装xgboost

win10+python3.5.2+xgboost 首先从https://www.lfd.uci.edu/~gohlke/pythonlibs/#xgboost下载xgboost-0.6-cp35-cp35m-win_amd64.whlamd64代表64位 进入下载完后的地址后pip install xgboost-0.6-cp35-cp35m-win_amd64.whl完事! 官方示例代码: import xgboost as xgb # read in data dtrain = xgb.DMatrix('agaricus.txt.train') print(dtrain) dtest = xgb.DMatrix('agaricus.txt.test') # specify parameters via map param = {'max_depth':2, 'eta':1, 'silent':1, 'objective':'binary:logistic' } num_round = 2 bst = xgb.train(param, dtrain, num_round) # make prediction preds = bst.predict(dtest) print(preds)

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册