其实写这个项目的初衷是想实践一下TDD开发,因为自己刚看完一本<<Test-Driven Development with Python>>,以前只是了解一点开发测试,看完这本书感觉这种敏捷开发方式非常适合我,自己完整写过一些小项目,但是大项目经常由于各种代码框架,代码规模搞得最后成了烂项目,而且关于TTD一些建议比如YAGNI(You ain't gonna need it)(你不需要这个)对于你对项目的规模有一定的控制.写这篇博客一方面将我开发hookmanTDD开发经历告诉大家,一方面希望更多人了解TDD开发,换一种开发方式,或许能让你找回编程的乐趣.

##引言

当然网上有些人对TDD测试开发嗤之以鼻,认为开发过程中不应该由测试驱动,应该先把所有的核心都先完善,最后在来完成项目.

在这里我想说一点我的看法,测试驱动是一种从核心到细节的开发方式.

用画一个人来打比方,给你一张白纸,TDD要求你先画一个躯干,这个首先要有个人样,当我们想画一个奔跑的人时,TDD要求你得给他再画两条裸腿,然后这两条腿怎么摆才能控制平衡,这两条腿穿什么鞋才比较生动,要接下来你慢慢测试考虑.

而我们传统的开发方式,是先从局部到整体,这种开发方式适合于小项目,而且像一个流水线出来的产物,比如说web开发,我们知道下一个要做的是视图层,然后模型层.但是这种开发方式不适合开发比如说一个新的软件,假如我们用传统开发方式,除非leader的掌控力非常好,一个越来越臃肿的项目很难坚持到最后,因为我们在项目完成的时候才能让项目真正的运行起来,在完成的过程中我们很容易迷失最后将项目烂掉.

而且我比较欣赏TDD开发的一个主要原因就是他同linux推崇的那种简单的软件开发文化很默契,我们在让我们测试通过时候,我们感觉在:欺骗自己”.

举个例子:

比如我有一个比较两个值谁大谁小的函数

def my_max(a, b):
    pass

我们写一个单元测试

Mytest(unittest.TestCase):
       test_my_max(self):
           m = my_max(1,2)
           self.aseertEqual(m, 2)

我们运行这个测试肯定失败,然后我们开始”欺骗自己”让测试通过,修改my_max

def my_max(a, b):
    return b

再运行测试,通过了,ok,”欺骗成功”,但我们用脑袋一想就知道,不行这个代码不对,但是咋办”测试通过了”,修改测试.

Mytest(unittest.TestCase):
        test_my_max(self):
            m = my_max(1,2)
            self.assertEqual(m, 2)
            n = my_max(2, 1)
            self.assertEqual(n, 2)

这下测试又通不过了,我们想要的功能就在一步一步测试拖动中慢慢实现,我们尽量用最简单的代码通过我们的测试.

看到这里有些人会认为我很傻,明明可以用两行代码(不能用系统函数max)

def my_max(a, b):
    if a > b: return a
    return b

轻轻松松通过测试,但是这反倒是TDD非常不推崇的,你一次走了太多步,你让你的代码跑到Test前面去了,其实很多程序员都会写测试,但是他们的测试很多都是基于代码的,当一个大功能实现的时候才开始测试,这个时候你会发现很多你的隐藏bug就藏在你一大堆代码里面,所以有些人为了排bug一行一行删代码来debug.

诚然很多时候我们感觉我们能毫无bug的完成一大段的代码,但是我们不能保证100%正确,而且或许今天能行明天就不行了,TDD推崇测试山羊精神,想象一只山羊行走在陡崖峭壁上,他只能走一步停一步,我们写代码也一样,我们前面是陡崖峭壁,我们不能保证我们下一步就不掉坑里,所以我们要学习山羊精神一步一步.

接下来关于TDD开发一些详细经历,我会每天抽出一点时候写.

hookman 是基于github上的webhooks开发的一个用Python写的小程序
,基于TDD开发.
如果你想向项目贡献代码,请看hookman 开发blog

简单用法

  1. 安装 pip install hookman
  2. 配置github的webhooks,设置监听服务器ip和端口3610(默认)
  3. 使用
    3.1 进入github项目目录cd /my/github/projectdir
    3.2 运行 hookman --run -d
  4. 关闭
    3.1 hookman --stop

版本差异

v0.1.0

  1. 基本实现监听端口
  2. 添加 run,stop,daemon,pidfile,logfile,projectdir选项

v0.1.1

  • 修复了在前台shell模式下的bug

隔离测试是相对与于整合测试来说的,现代软件架构流行分层式、模块化,而隔离测试就是相当于在每层上进行测试,整合测试就是跨越多个层进行测试

introduction

举个简单例子来说,在django系列中,我们把表单提交分成两个层,一个form层,一个式model

form层相当于接近用户交互层,而model层与数据库联系更大, form层负责获取用户数据并验证,而model层根据form层数据将数据存入数据库。

隔离测试就是隔离form层向model层提交,而整合测试就是直接测试form层和model层。

判断一个测试是整合测试还是隔离测试就是看测试的边界,整合测试相当于我们更加熟悉,我们测试时通过伪造form提交,然后通过数据库获取存入数据来得到验证,而隔离测试就比较复杂,因为我们很难在一个耦合度高代码找到怎么隔离两个层的方法。

接下来我就介绍python里一个神器:mock

自己用过其他语言框架中的mock,但是python里面mock里面最神奇的的是里面的patch,就像一个超级补丁一样。

Example

接下来我用一个例子来介绍一下如何写隔离测试

首先我们在 lists.forms.py中一个表单model

class ListForm(forms.models.ModelForm):
    def save(self):
        pass

我们在lists.models.py有个model

class List(models.Model):
    def create_now():
        pass

现在来分析一下这个隔离测试,我们要测试ListFormsave方法

首先ListFormList两个类是耦合的,一个整合测试,我们只要调用save方法,然后查询数据库就可以完成这个测试,然而隔离测试不同,我们只能测试在调用save方法时,他“干”了什么。他可能调用了Listcreate_now方法,将得到表单数据传了过去。

意味着我们只能测试到save方法调用了List方法。

那这个隔离单元测试该怎么写?

接下来我们隆重介绍mock里面的最强补丁patch

根据TDD原则,我们先新建一个单元测试IsolutionFormTest

import unittest
from unittest.mock import patch, Mock      ##load super patch 
from lists.models import List
from lists.forms import ListForm


class IsolutionFormTest(unitest.TestCase):

       @patch('lists.forms.List.create_new')
       def test_save_creates_new_list(self, mock_list_create_new):
            form = ListForm(data={'text': "example text"})
            form.is_valid()    # get clean data
            form.save()

            mock_list_create_new.assert_called_once_with(
                text= "example text"
            )   # the major test

我来介绍一下这个patch,就像名字一样补丁,通过我们使用字符串将要替换的函数写出来,当test运行时,会自动将函数替换成一个mock对象,通过参数(上面的mock_list_create_new)赋给函数。

你可以这样想象,当form.save()调用时,在save函数里面,我们如果使用了lists.forms.List.create_new这个函数,这个函数就会被直接被补丁替换掉,你如果使用了lists.forms.List.create_new(text="xxx")就会变成mock_list_create_new(text="xxx"),当你调用了mock_list_create_new(text="xxx")时,mock_list_create_new这个mock对象就会记录下来。

这样我们就通过mock_list_create_new测试了函数是否执行了没有,因为隔离开form层和model层的就是通过两者之间的接口。我们只用测试接口是否执行了没有就可以了。

这样我们就完成了隔离单元测试,运行一下肯定失败,我们接下来就把save方法完善一下通过测试。

from django import form
form list.models import List

class ListForm(forms.models.ModelForm):
    def save(self):
        List.create_new(text=self.cleaned_data['text'])

ok测试通过了,我们就可以歇一口气了。

Conclusion

相对于整合测试,隔离测试运行速度更快,但是相对的隔离测试对接口要求非常严格,好的方面利用我们进行更好的代码设计,更好的分析代码的复杂程度,并且当接口变迁的时候,隔离测试能迅速发现变化而报警,然而隔离测试工作量比较大,而且没有整合测试那么好理解。在实际生产中对于复杂的接口我们尽量进行隔离测试,对于简单接口我们使用整合测试能根据减少程序的耦合性,而且能迅速发现集成问题。

ps:对patch感兴趣的童鞋可以自行google,patch好玩的地方还有很多,这里为了篇幅我只介绍了最核心的使用方法,大家可以自行探索patch更多好玩的东西。s

看了 << Python Web 开发 测试驱动方法>> 以后, 感觉自己找到自己的一些项目之所以不能够很好的 维护下去的原因.总结了一下自己的理解, 记录下来.

什么是 TDD

Test-Driven Development

TDD 是一种以测试为驱动开发的方法, 自己以前也听到过这个名词, 但是平常只是稍微写一点测试,并没有让测试领导开发节奏.

简单来说TDD是先写测试再写代码, 这于我们平时开发的时候有很大不同,比如我们写一个web页面, 我们一般是先写好代码,然后在浏览器上调试(相当于测试),然而测试驱动开发就不同了,我们一开始只写测试, 然后写代码让他通过我们的测试.

TDD的主题基本上很多人都知道,但是具体步骤和诀窍很多人不明就里.下面来详细介绍怎么让Test
引导你的开发.

怎么进行TDD

首先让我来看一看Test是什么, Test 分两种

  1. 功能测试
  2. 单元测试

功能测试

很多人知道单元测试却很少听到功能测试, 功能测试在开发过程中经常被人忽略,但是功能测试能很好的把握开发方向

功能测试就是从用户角度出发, 从用户的角度测试代码.

用web项目开发来打比方, 用户只能通过浏览器来浏览你的web, 所有的交互只能通过浏览器来实现,功能测试就是模拟用户进行浏览器上的操作,具体来说我们可用使用selenium来操控firefoxchrome,通过我们使用selenium提供的接口操作浏览器访问页面获取html, 来进行功能测试.

功能测试就是测试一个项目成品的功能, 在平常的项目开发中,这个测试往往是提到项目完成之后人工进行, 然而在TDD中这个却被提到了最前面,他成了一个风向标,所有的代码目的都是为了实现这个功能测试.

单元测试

单元测试这个很多人都很了解, python里面有unittest 这个单元测试框架.就是专门为单元测试而开发的.

单元测试历史悠久,很多人都写过单元测试,不过大部分人写单元测试都是在函数或者类完成之后写的.

在TDD中,单元测试进行最频繁的测试, 在功能测试完, 写每一个函数都提倡先写单元测试, 然后进行开发.

单元测试关注点与功能测试不同, 单元测试注重的是每一个函数执行的结果, 给定一个输入就一定要得到一个确定的输出,比功能测试他更关注底层代码,毕竟功能测试只关注用户最后得到结果,单元测试将你的代码函数形成一个单元,逐个运行,逐个测试.

前面介绍了一大堆概念,却没有落到实处, 功能测试很简单,我们评价一个项目,能很快的写出测试方法,但是对于单元测试来说,我到底该怎么进行,这同我平时的开发有什么不同,下面就详细的介绍单元测试的几个重要的要点.


留个坑慢慢填.

最近这几天在开发一个hmtl5的游戏, 但是对于js怎么使用面对对象来编程有点困惑,查了一些资料
整理如下.

js的this用法

  • 非对象属性函数(内部函数)

    var point = { 
     x : 0, 
     y : 0, 
    moveTo : function(x, y) { 
        // 内部函数
        var moveX = function(x) { 
    this.x = x;//this 绑定到了哪里?
        }; 
          // 内部函数
        var moveY = function(y) { 
            this.y = y;//this 绑定到了哪里?
         }; 
    
         moveX(x); 
       moveY(y); 
     } 
    }; 
    point.moveTo(1, 1); 
     point.x; //==>0 
     point.y; //==>0 
     x; //==>1 
    y; //==>1
    
  • 对象属性函数
var point = { 
        x : 0, 
        y : 0, 
        moveTo : function(x, y) { 
                this.x = this.x + x; 
                this.y = this.y + y; 
                             } 
         };

 point.x; // ==> 1
 point.y; // == >1 

 point.moveTo(1, 1)//this 绑定到当前对象,即 point 对象
 point.x; // ==> 1
 point.y; // == >1 

其实this的用法还有很多,我为什么只列出上面两种是因为所有的this用法都可以归为这两类.

非对象属性就是这个函数不存在对象的内存空间里面的函数
对象属性就是说这个函数存在于对象内存空间的函数

这样说很绕, 抽象的来说, 把每个函数都看做是对象, 我们把这个对象point想象成一个上锁的柜子A, 每个变量名就是一把钥匙, 我们这个柜子A里面可用放很多钥匙,每个钥匙又对应着其他柜子,我们也可以放东西,但是这些东西只能是一些数字字符串什么的,这就对应着对象的这种继承性,

现在我们来看放在柜子A里面的钥匙,有一个钥匙可用开其他某一个的柜子B(相当于对象的属性的moveTo函数),当我们就在柜子B里面使用this时,这个this是什么呢.

这个this就要找一个柜子, 那为什么要找一个柜子(对象呢)

这就是js语言设计的松散, 当在非严格模式下, this 会被强制转换成一个对象, 对于例子一,因为内部函数 this 并没有给他赋值(你可以把他看做一个我们找不到他的钥匙的柜子), 所以this被强制转换成了全局的柜子(全局变量)

ps: 严格模式在函数或变量前加上 'use strict';

怎么解决这个问题了, 有两种方法, 不是没有赋值给this嘛, 我们可用call来给函数内部的this赋值,

moveX.call(this, x);   // 我们把当前函数的this赋给内部函数

但是这种方法不够优美, 我们还可以直接把 this用个变量that给内部函数用,

var that = this;
var moveY = function(y) { 
        that.y = y;//this 
 }; 

这样这个this 就不会被强制转换成全局变量了, 当然你可以开启严格模式, 这样this的话就会变成undefined, 你也不会因为你的代码问题而污染全部变量.

总结

js 的 this 是面对对象编程的一种体现, 但是js的this由于有点不严格,所以有时候会出现一些令人意向不到的结果,

引用

这段时间重新学习了web前端的技术栈,其实一开始加入社团时就是从前端开始入门的,等到了会仿写几个页面后就跳到了后端的坑,从html、css、js(只学了一点)又马不停蹄的转到.net平台、后来在老司机的带领下我们放弃了不开放的.net平台投入了python的怀抱

python的确很好,“内裤”很多,相对于.net 相对于封闭的生态圈, python对第三方的类库的依赖很大,pip 是程序的常客,看到一个好的类库就pip下来了。

学了不少程序语言,从静态语言到动态语言,这其中的转变刚开始的确让人很苦闷。

自己从c系的语言过来,刚接触C的时候对于“过程编程”有很深的体会,结果并不重要,重要的是过程,有时候为了写好一个完美的函数自己苦思冥想好几天,花在一个函数上的时间比项目的时间都多。

然而到了学习python, 自己把C系的学习习惯带到python , 在做项目的过程中,当自己写完一个函数的时候总在想怎么优化代码,怎么抽象化对象让事情简单,慢慢的走入一个误区,让我忘记了python的口号 life is short, I use python .

python是一门目的性很强的语言, 先让我实现功能,其他以后再说, 我以前使用的时候对过程强调的太多了, 一个函数我得测试测试很多遍,确定每个参数的作用还有影响, 花了太多时间, 适得其反,我的代码一点都不 pythonic

我在学习 python的过程中走了很多误区,总结起来有几点:

  • 太注重过程,不注重结果
  • 太注重功能丰富,不注重简单

在我看来, pythonic 就是用最简短清晰的代码最快的完成自己的目的

python和javascript

这个都是动态的脚本语言,javascript更倾向于脚本

两个家伙在我看来都是鸭式语言, 当然js更倾向函数式,而python更倾向于对象式.

python 是目的性强, javascrip 是表现力强

以前在后端的时候总觉得只要把自己那端的语言学好就行了, 任何事python都能用来解决, 无论是数据库还是服务器,就算是图像都能用python处理(使用PIL),虽然有着GIL,有这性能低下等等的问题 .
颇有一番”学好数理化走遍全天下都不怕”的念头,

然而在实际项目中慢慢发现, 计算我能用几百行python代码画出一朵花来,用js几句话就在canvas上弄出来了,而且很轻松就能换成其他的东西.

得益于node.js社区的火热, 现在javascript也能在后端大显身手了, 学习了一点node.js,感觉通过node.js对js的封装,让javascript变成了一门类python的语言, 原本js文件只能运行在浏览器里面,通过node.js将每个文件封装成模块,就像python将每个文件封装成package,通过模块与模块的合作,js也能像python那样简单的用几行代码就构成一个强壮的服务器(虽然说现在node.js有点不稳定,但是随着node社区的努力也能将node.js强壮起来)

总结

通过一个星期的重温js, 自己简单的涉猎了现在很火的Angular,Ionic.和Node.js,并简单的搭建 Electron + Cordova + Ionic + Angular 原来博客 的 Desktop端
(linux + windows + OS)的软件版,还有移动版(由于本人没有OS操作系统,只做了Android版),还花了半天时间搭建了这个基于hexo的博客,算是完成了全平台的搭建

接下来我会自己工作前的大学时光好好的培养自己解决问题的能力,希望能在工作前爱上并享受自己将要做的工作!


string

原来的很多函数都逐渐迁移到strunicode对象上去了,
不过有两个函数没有迁移出去


第一个是capwords

个人觉得没什么卵用
就是将英文单词首字母大写

比如

string.capwords('this are some words')

输出为 This Are Some Words


第二个我觉得挺有趣,他可以帮你把对应的文字换成你设定的

我们可以用它来设计出莫斯密码’=’.

"""create a table"""
table = string.maketrans('abc','123')
print 'abc123'.translate(table)

输出为123123成功把abc转成了123


string还有一个模板类型Template
这个类型同我们转义差不多(%),不过能够实现的
更加自主化
我们可以继承这个类来修改模板类的具体实现
而且这个模板类有一个


textwrap

由于sublime输出一个很长的字符串很卡,这个格式化字符串的类能够帮我们
解决很多问题

我一般去除整体缩进然后去填充字符串

dedented_text = textwrap.dedent('...a long word...')
print textwrap.fill(dedented_text, width=50)


由于网站要实现图片和文字的混排的上传
所以在网上找了富文本编辑器的插件,发现CKEditor这款还不错的插件

我用的是4.5.1这个版本,的确很好用,只是引用了一个js文件就可以实现。


  1. 将网上下的CKEditor包解压放在根目录下
  2. 在页面上引用CKEditor的核心包ckeditor.js
<script src="ckeditor/ckeditor.js" type="text/javascript"></script>;
  1. 在页面添加一个输入框textarea

    <textarea name="individual" id="individual" runat="server"></textarea>
    <script type="text/javascript">
     CKEDITOR.replace('individual');
     </script>
    

只要通过上面上面几个步骤就能实现富文本编辑器,但是点开图片上传功能,发现只有上传url的功能,并不能本地上传图片,百度了一下发现由于安全性问题CKEditor没有上传功能,只有安上CKFinder才能实现上传功能,于是我在官网下了ckfinder__aspnet_2.5.0.1,同CKEditor一样引用JS文件(只要引用ckfinder.js)

<script src="ckfinder/ckfinder.js" type="text/javascript"></script>
  • 由于我是MVC的网站基于.net4.5,在官方给的包里面有一个asp.net网站实例放在_source文件夹里面,里面有一个基于.net2的示例网站,一运行就报找不到 System.Web.UI.Design这个命名空间的错,所以我把它从项目中排除,把项目中bin中debug文件夹下的CKFinder.dll复制出来,引用到我自己的项目中。

    接下来要配置CKEditor来让CKFinder引用进来,在CKEditor文件夹下config.js在CKEDITOR.editorConfig = function (config) {};方法中添加如下代码:

    config.filebrowserImageBrowseUrl = ‘ckfinder/ckfinder.html?Type=Images’;
    config.filebrowserFlashBrowseUrl = ‘ckfinder/ckfinder.html?Type=Flash’;
    config.filebrowserUploadUrl = 'ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Files';
    
    config.filebrowserImageUploadUrl = ‘ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Images’;
    config.filebrowserFlashUploadUrl = ‘ckfinder/core/connector/aspx/connector.aspx?command=QuickUpload&type=Flash’;
    config.filebrowserWindowWidth = ‘800’; //“浏览服务器”弹出框的size设置
    config.filebrowserWindowHeight = ‘500’;

注意在配置Url的时候要修改成相对于网站本地网站磁盘文件路径,比如说,你的网址是http://example.com.cn ,你把ckfinder文件夹放在Admin下的Editor文件夹,那么所有url要改成下面类似的格式

config.filebrowserImageBrowseUrl ='/Admin/Editor/ckfinder/ckfinder.html?Type=Images';

否则会报404错误,

最后一步是修改一个函数让所有人能看到服务器上传文件夹里面的文件,在ckfinder文件夹下面的cofig.ascx文件,找到 CheckAuthentication函数将返回值改成true

当然如果你想修改上传文件的地址,你可以在上面方法里面找到SetConfig()方法,找到BaseUrl,修改为你想上传的地址,

—–

PS:

我是在在VS里面进行调试的,由于VS的IIS在调试的时候不允许对磁盘文件的路由地址访问,就是CKFinder通过ckfinder.html这个html来实现上传图片的功能,但是这个在调试的时候VS无法访问这个文件,所以一直报404错误,可以修改IIS来允许IIS访问磁盘文件,步骤如下:

  1. 右键点击IIS Express,选择显示所有应用程序
  2. 找到运行网站的配置,进入applicationhost.config文件夹
  3. ctrl+f 寻找UrlRoutingModule
  4. 将preCodition设置为空字符

    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
    

如果你是用MVC进行表单传值的话,你必须在post方法上面添加 [ValidateInput(false)] 属性,如果不这样的话就会报下面的错

“/”应用程序中的服务器错误。
从客户端(content="<p>sdfsdafwewo shdfh...")中检测到有潜在危险的 Request.Form 值。

最近在学利用python进行数据分析,发现pandas这个库对数据的处理非常灵活,
而且号称python的maltlab的.其在图形绘制也非常方便

在Windows下安装python关于科学计算的库非常不方便,还好有一些公司做好了关于python计算库在Windows安装的安装文件,这里我推荐anaconda,我推荐安装32位包,因为64位有很多库不支持.

pandas内部是使用numpy做存贮结构的,所以我们必须先了解一下numpy

先谈谈我对机器学习的理解

什么是机器学习?

我们人类有从婴儿开始就开始学习,父母教我们穿衣吃饭、老师教我们读书写字,我们开始能辨别好人坏人,开始通过自己的经验来判断新事物。

机器学习很简单,就像人一样,我们教机器通过我们教的来判断新的事物,或者在从新的事物里面学习处理新的事物。

这看起来很复杂的样子,但是从我们神经网络来看,我们可以把学习当做建立一个神经元连接,通过输入的信号得到一个输出的信号.我们只要简单的把输入的信号分类就可以了.通过无数个分类我们就可以建立复杂的神经系统,进而实现’学习’这个功能.

如何分类?

涉及到分类,假如输入的信号种类只要两种,我们就可以简单用if-else来实现分类功能,但是有时候输入信号种类个个都有细微的差别,只是遵循某种规律,这时候我们不能用简单的if-else来进行分类了,下面我就按照书的顺序来解释各种强大的分类方法.

K-近邻算法 ( k-Nearest Neighbor )

作为本书的第一个机器学习算法,K-NN算是我感觉原理最简单的一个了.

假设我们有两个点, 红点为(-1, -1)分为红类, 绿点为(1, 1)分为绿类
图片1

接下来我一个点(0, -1),这个点应该分为红还是绿呢,我们添加两条辅助线

蓝点离红点距离为1,蓝点离绿点距离为2.2,我们很轻松的可以知道这个点应该分为红类.
现在我们进一步推广,当有很多种类点的时候,当我们二维扩展到N维,给一个点a我们只要选取距离a最近的K个种类,我们就基本能判断他属于这K个种类的,这就是K-近邻的原理了.

K-近邻算法是最简单最有效的算法了,但是他也有缺点,比如他必须保存所有训练样本的数据,当训练样本很大的时候就会占用很多内存空间,我们后面会学到的KVM只取支持向量的训练样本来计算可以减少很多占用内存

而且K-近邻算法对训练数据集都要计算距离值,实际使用可能会非常耗时,我们后面学到的logistic回归能很好解决这个问题.

######### 总而言之,KNN作为小样本时非常简单粗暴,但是他无法给出任何数据的基本结构信息.接下来我们要学习用概率测量解决分类问题,这个算法能解决这个问题

(决策树)[]