由于网站要实现图片和文字的混排的上传
所以在网上找了富文本编辑器的插件,发现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 值。

最近从python2转到python3,发现还是有一些不同,一些库改名字很好解决,但是这个import机制不了解原理是不好理解的.

python2是默认相对路径导入,python3是默认绝对路径导入

首先这个包的导入机制,就是你在一个module里面引用另一个module,python运行文件有两种方式,一种是直接以主文件运行(默认以这种方式运行,同下面一种有点区别),一种是以module形式运行,就是用python -m filename方式调用.

以module的方式运行

####### 什么是相对导入和绝对导入呢
相对导入是用一个.来声明的,相当于Unix上的选择当前文件夹.

假设你的文件目录为下面的

main
|    main.py
|     __init__py
|   momod+
|      | __init__.py
|      | pack.py
|       | flask
|      |    |  __init__.py
|       |    |  myflask.py
--------------------
python里面的module分三种,一种是build-in module(内建库),一种是第三方库,还有一种是你自己写的库(如上面的flask).

在python2里面,当你import 一个module时,搜索的顺序是 内建库,自己的库,第三方库.

####### 而在python3里面顺序为 内建库,第三方库,你自己的库.

我自己感觉python3的import的机制更为清晰,因为当你import一个库时,假如你写的库和第三方库重合时,你优先导入第三方库,如果你不适用声明相对导入的话,你无法正确的导入自己的库,而使用.来声明库来自自己的代码让代码的结构更加清晰了.所以如果你想让你的代码兼容py2和py3,你自己的库都要采用相对导入方法来导入.
比如在momod 里面的pack.py假如想引用flask的myflask.py要这样写

from .flask import myflask

########### 假如你在py2中写了 from flask import myflask(并且你安装了flask库),这个是可以成功运行但是在py3中就会报错,因为他会优先导入flask库假如你没有显示声明相对导入的话.


上面成立的前提是将pack.py以module方式运行,或者运行main.py在其中引入pack.py.接下来讲讲以主文件运行的不同.

当你直接使用

python pack.py

你假如在pack.py里面使用了这个

from .flask import myflask

引用了自己的myflaskmodule,在py2和py3中都会下面报这个错

SystemError: Parent module '' not loaded, cannot perform relative import

因为当你以主文件方式运行 pack.py ,python会吧pack.py重命名为__main__,所以用.相对路径也不会是当前文件.所以全部都只能用绝对引用.所以在主文件运行在python3里面有个问题,假如你自己的库与第三方库有重名.

python3默认绝对路径,自己的库不会优先于第三方库被扫描.有两个解决方法,把自己的库重命名,第二个方法就是把包含主文件的文件夹加上init.py,你可以在sys.path的路径里加上..或者具体上一个上一个文件夹的路径.怪绕口的,其实你只要python能找到你的上一个文件夹,就行.

说到这里顺便插一句对doctest和集成测试的理解.

由于我平时喜欢一边写代码一遍测试功能,图方便就使用doctest直接插在方法里面,在代码后面加上

if __name__ == '__main__':
        import doctest
        doctest.tesmod()

平时写小module的时候没有问题,在将python2转python3时候,出现问题,因为我要测试这个module时,会报上面的那个错,因为我要测试他的话必须将它作为主文件.查了资料知道,其实doctest虽然在当前页面代码测试,但是对于module的话,最好采用集成测试,一是module很多,假如一个一个运行很麻烦,二是有时候module必须多个一起测试,所以测试module时要用集成测试来取代doctest.

###

最近学了两个python库,一个负责管理线程,一个负责管理进程,原来一直写的都
是些单线程的程序,虽然web也关于并发和多涉及到线程,但都是框架管理的,学习>过后发现了解线程和进程对python的web开发也有一定帮助。下面先谈谈这对python对线程和进程的支持再谈谈对这两个库的应用。

python对线程的支持并不是非常好,所以你可以在很多文章上批评python的多线程的弊端,但是为什么python对多线程支持不好呢,为什么其他语言比如

静态语言没有这个弊端呢。

首先我们要知道python是一种解释性语言,每段代码都需要解释器编译运行,解释器有很多种最主要的是CPython,其他还有IronPythonJython,官方的是CPython解释器,我们一般说对多线程支持不好的就是说的CPython解释器(用的人最多就省略成python解释器),python解释器为什么对多线程支持不好呢,是因为GIL的存在,当然这个存在就是因为这门语言的的特性产生的。

GIL是什么呢,下面是官方的解释

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

就是GIL是python的互斥锁,简单的理解就是代码会锁住python解释器。理解代码的锁定是什么必须要先了解什么是多线程

多线程表示一个主线程,多个子线程,主线程是程序执行时系统自动给你申请的一个线程,而子线程我们可以理解为一个代码块,我们可以充分利用硬件的支持比如说多核,让一个CPU执行主线程,其他CPU执行子线程,通过操作系统的虚拟内存技术让所有线程共享相同代码空间达到提高代码效率的作用,我们可以通俗的把一个进程比作一辆火车,车厢头为主线程,每节车厢为子线程,只要你车厢(子线程)越多,你运的货物也越多,但是也要考虑硬件的方面,

了解完多线程是什么我们就可以解释GIL对多核CPU工作性能的影响了,在单核CPU里面,主线程在释放GIL的时候,把CPU让给子线程,子线程代码块得到GIL,然后执行,这样就能充分利用CPU,这个GIL对单核性能的发挥没有影响,能得到100%的利用,但是在多核的的时候就有问题了,假如主线程的代码一直需要解释器来执行,
比如说下面

GIL.acquire()
try:
    while True:
        do_something()
finally:
    GIL.release()

主线程代码对GIL的锁定和解开只间隔很小的一个系统时间,子线程在其他CPU核心得到GIL解开后CPU的调度命令后才能被唤醒,但是当唤醒后,主线程的代码又锁了GIL,然后只能等待主线程下次调度命令,但是到了切换时间又切换回去到待调整状态,一直处于唤醒,等待的恶性循环,多核的功能完全没有发挥出来而且还比单核更加差,所以python因为GIL的存在对密集型的线程支持不佳,但是假如主线程是在执行想web这样等待用户输入,而不是每分每秒都在使用解释器执行代码,多线程的优势就能发挥出来。

解决方案

GIL作为解释器的一个Bug一样的存在,我们也有一定的解决方法,开线程,和用Ctype绕过解释器是我们一般的解决方法,你想了解更多可以看这个
接下来主要解绍用multiprocessing来绕过多线程的瓶颈

线程锁和进程锁

为了实现线程安全,我们也要借助锁的存在,我们先用下面的代码来验证一下多线程对于线程安全的问题。我们声明一个线程锁 threading.Lock(),
class Counter(object):
    def __init__(self, start=0):
    self.lock = threading.Lock()
    self.value = start

def increment(self):
    logging.debug('Waiting for lock')

    self.lock.acquire()
    try:
        if self.value < 8:

模拟负载

            logging.debug('Acquired lock')
            self.value = self.value + 1

    finally:
        self.lock.release()
def worker(c):
    for i in range(2):
        pause = random.random()
        logging.debug('Sleeping %0.02f', pause)
        time.sleep(pause)
        c.increment()
    logging.debug('Done')
counter = Counter()
for i in range(20):
    t = threading.Thread(target=worker,args=(counter,))
    t.start()
main_thread = threading.currentThread()
for t in threading.enumerate():
    if t is not main_thread:

保护线程

得到value值

我们运行之后得到counter.value值为8,这很好理解因为我们限制了它的大小小于8时才自增1,但是如果我们把锁去掉呢,我们把self.lock.acquire()``self.lock.release()都注释掉,得到的结果却是一个21,而且每次运行的结果都可能不一样,由于线程在实现自增的时候有一定的时间(time.sleep(2)),所以当多个进程执行的时候当他们从堆栈上取到counter.value值都为7时,这时候他们都满足
counter.value小于8,所以都执行了自增,在系统负载2秒之间(time.sleep(2))有多少个线程执行就会逃过我们给他的限制,这样就造成了线程的不安全,但是我们给他加上锁之后,无论开多少个线程,最终结果都是8。在python里面我们线程锁和进程锁我们可以看做是同一种东西。

ps:当同一线程相互争夺锁时,失败的会进出线程队列等待锁解开。

线程进程工作方式

单行

单行主要通过锁来实现,线程通过锁threading.Lock()对象创造锁,进程通过multiprocessing.Lock()对象创建进程锁,单行操作一般都是对共享数据修改的一种保护。

并行

并行操作是一般是对数据的一种共享,一般不对公共数据涉及修改,我们可以创造很多线程和进程一起并行操作,也可以限制线程和进程的并行数量,两种方式选择主要是判断代码类型是I/O密集还是线程密集型的。如何限制并行数量我们可以通过threading.Semaphore(sizenum)(进程为multiprocessing.Semaphore(sizenum))我们可以控制对共享的线程数量。进程提供了一个进程池的类型(multiprocessing.Pool),我们可以创建一个维护了一定程的进程池,但是他同时并行的数量并没有控制,只是帮我们创建了这个进程池,每个进程并不是只执行一个任务,可能执行多个方法通过一个进程.

单行混合并行

单行和并行混合我们可以通过在代码中设置锁来实现,当然python给我们提供了两种对象来实现单行和并行的控制,线程的是threading.Event()threading.Condition(),进程的是multiprocessing.Event()multiprocessing.Condition() 两种对象都是提供了一种命令指令,但是Event对象可以用来判断命令是否下达而做出相应的反应,而Condition对象更倾向于当命令下达后才执行并行的操作。

线程和进程通信方式

当我们想让线程和进程共同执行一些固定的任务,我们就需要线程和进程之间能够通信,线程和进程通信我们使用队列(Queue),进程和线程的Queue有点差异,就是进程Queue传递的对象必须pickle化,而且为了能够使用join()(保护进程)task_done(通知任务完成),我们一般使用JoinableQueue
代替Queue在进程中。

Queue对象之间通过putget通信,我们把任务put上去,Queue自动分配给当前的线程或进程,
这样就能实现对任务的流水作业话。

引用

12/26/2015 10:50:21 PM GIL维基资料

GIL博文

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

什么是机器学习?

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

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

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

如何分类?

涉及到分类,假如输入的信号种类只要两种,我们就可以简单用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作为小样本时非常简单粗暴,但是他无法给出任何数据的基本结构信息.接下来我们要学习用概率测量解决分类问题,这个算法能解决这个问题

(决策树)[]

泛型对于解决面对对象编程的算法设计可以提高其运算速度,但是对于引用类型来说还是没什么差别,因为引用类型只是指针的地址的调用,简单来说泛型还是挺好理解的,但是对于泛型、非泛型、继承和接口的融合就有些迷惑了。

比如说这种接口
public interface IEnumeratot<T>:IDisposable,IEnumerator,ICompare<T>

这个泛型接口继承了两个非泛型接口,和一个泛型接口。


我一开始理解泛型就是一个个模型,只要我们把类型一个参数赋给他,他就能生成一个标准的类型,他缺少的只是一个参数而已,我们引用的时候感觉就像我们引用一个“全体方法”,把参数赋给类型后就可以一直调用类中的方法了,但是对于接口的继承如何理解?

对于泛型类的继承,继承的类必须实现泛型的参数或者保留泛型的参数,比如下面

  public class A<T>
{
    public T tt;
}
public class C<T> : A<T>
{
    public T tt;
}

或者是这样

  public class A<T>
{
    public T tt;
}
public class C : A<string>
{
    C cc;
}

对于泛型继承非泛型类,比如下面

public class A 
{
     A aa;
 }

 public class B<T>:A
{
    B<T> bb;
}

基类是非泛型,而继承的是泛型类,我感觉这种构造就是让泛型类多了一种包容性,比如下面的链表实现的代码,让基类是非泛型,而继承是泛型,就能让链表可以连起很多种类型的数据,而本身的类型安全没有丢失。

public class Node
{
        pretected Node next;
          public Node(Node next){
            this.next=next;
            }
}
public class TypeNode<T>:Node
{
    public T data ;
    public TypeNode(T data):this(data ,null){
        }
    public TypeNode(T data,Node next):base(next){
        this.data=data;
        }

    }

泛型的约束

管线命令 (pipe):

  1. 撷取命令: cut, grep
  2. 排序命令: sort, wc, uniq
  3. 双向重导向: tee
  4. 字符转换命令: tr, col, join, paste, expand
  5. 分割命令: split
  6. 参数代换: xargs

    分割文档

长长的一大片文档有时我们并不愿意看到全部内容,我们只想关注
部分内容的时候了可以考虑使用分割文档命令

cut是一个很好的分割文档工具

vi常用命令

  1. [Ctrl] + [u] 屏幕『向上』移动半页
    • 光标移动到非空格符的下一列
    • 光标移动到非空格符的上一列
  2. n那个 n 表示『数字』,例如 20 。按下数字后会向右移动这一行的n 个字符。例如 20<spac移动 20 个字符距离。
  3. 0 这是数字『0 』:移动到这一行的最前面字符
  4. $ 移动到这一行的最后面字符处(常用)
  5. H 光标移动到这个屏幕的最上方那一行
  6. M 光标移动到这个屏幕的中央那一行
  7. L 光标移动到这个屏幕的最下方那一行
  8. G 移动到这个档案的最后一行(常用)
  9. nG n 为数字。移动到这个档案的第 n 行。例如 2档案的第 20 行(可配合 :set nu)
  10. gg 移动到这个档案的第一行,相当于 1G 啊! (
    n n 为数字。光标向下移动 n 行(常用)

首先谈谈自己对EF的接触的过程吧,最先接触EF只是因为EF支持从数据库把关系扒下来,可以省掉自己写Select、Update、Insert这些SQL语句,而且修改非常方便,后来在使用的过程中发现导航属性这个关系,然后才慢慢知道数据库的索引是什么,由于自己接管的是大学生社团的数据库,大多时候创建者并不会考虑表的联系,一般创个主键就完事了(顺便吐槽一句,握草,数据库的表名和列名是什么鬼全用拼音首字母,为了兼容前面的内容我们还得花一半时间猜你们的列名,简直醉了,除了ID这个英文他们会,你们的英语是体育老师教的吗???)言归正传,用EF的确学到了对数据库表的的建立的理解,毕竟自己刚学数据库的时候就是把所有的字段塞到一张表里面,刚开始自己使用EF从数据库拔下来的表然后修改实体的关系的数据(感觉其实就是使用EF的EMDX的Code First),使用这个并没有出现很多问题,后来又接触完整的Code First,就是直接用代码生成数据库,虽然中间遇到无数的BUG但是这些BUG让我对数据库和EF的关系有了更深的理解,话不多说,直接上BUG。


1. EF未能确定外键,请用注解属性或Fluent API标记外键

网上关于如何用代码的(Fluent API或注解属性)指定外键的文章有很多有很多。在这里我想谈谈对外键的理解,首先建立起一张主表


主表














列名

类型

ID

int

Name

nvarchar(50)

首先ID是独一无二的,而Name不是(重名的有很多),当我们给ID套上主键的时候,这时候插入这张表的ID只能有一种(这是数据库的一种约束,当然你可以不选择这种约束),一个人除了姓名还有其他东西,假如这时我们还有帮他加入性别这个信息,我们可以修改上一张表添加一个字段,也可以新建一张表存贮性别这个信息(当然在实际生活中只用一张表存一个信息很少),我们新建的这张表是这样的,


附表










列名

类型

Sex

bit

这张表存贮了性别这个信息,但是如何将他从主表联系起来呢,我们先提取主表中的ID作为联系(我们称为外键)表改为


附表














列名

类型

ID

int

Sex

bit

我们把列名ID设为主键,这样我们就建立了一对一的关系,这个附表的ID必须不为空,这种关系还有一种就是将外键存贮在主表里面,就是将主表里面添加一个外键SexID,主表和附表要改成下面这种


主表




















列名

类型

ID

int

Name

nvarchar(50)

SexID

int

附表










列名

类型

Sex

bit

现在这种结构就是外键SexID可以为空(注上面的外键不能为空),

ps:说到外键不能为空我插一句,有些教科书上说外键不能为空也是对的,外键只是一个列名,当这个列名不唯一(也就是不为主键的时候)这是外键可以为空,为空的含义是不确定对应主表的值。

现在开始谈谈这种情况在EF发生的原因,你吧主表设为Person表,附表为SexInfo表,对应的代码如下

public Person{
    public int ID{get;set;}
    public string name{get;set;}
    public virtual SexInfo Sex{get;set;
                                            }


public SexInfo{
    public int ID{get;set;
    public bool Sex{get;set;
    public Person person{get;set;}
                            }

这个时候EF无法判断哪个是主表那个是附表,就是无法将外键加在哪个表的ID上,或者像上面的表中在Person表中添加一个外键。也就是在这种情况里面有四种可能的情况

  1. 在Person表里面添加一个外键(假设为Person_SexInfoID)
  2. 将Person表中的ID设为主键和外键
  3. 在SexInfo表中添加一个外键(假设为SexInfo_PersonID)
  4. 将SexInfo表中的ID设为主键和外键。
注假设在EF中没有给属性添加[Key]注解属性或在Fluent API中声明一个属性为主键的话,EF会自动将有ID后缀的属性设置为主键并让他为标志字段自增,还有表中没有主键无法导入到EF中。

虽然EF有自动检测代码生成关系,但是本人还是比较推崇自己在Code First时就想好外键,这样在用模型绑定的时候就不会发生一些很可能发生的错误。在这张表里面为了节约数据库空间最好在SexInfo里面添加一个外键,现在我就来谈谈分别在两个表里面添加外键可能会遇到的BUG。

  1. 在SexInfo里面添加外键PersonID

类修改成为

public Person{
    public int ID{get;set;}
    public string name{get;set;}
    public virtual SexInfo Sex{get;set;
                                            }


public SexInfo{
    public int ID{get;set;
    public bool Sex{get;set;
    public int PersonID{get;set;}
    public Person person{get;set;}
                            }

然后我们可以选择在PersonID上加上[ForeignKey("Person")][Requird],或者在重写的OnModelCreating方法中加入 这样一句代码

modelBuilder.Entity<SexInfo>().HasRequired(x => x.Person)
.WithRequiredPrincipal(x => x.BindingRole).HasForeignKey(x => x.MenusManageID)        

其实我更推崇写Fluent API 来约束,因为将注解属性放在Model里面太乱而且容易错,比如说假如你在PersonID上面少注释了一个[Required] 你又会得到一个模型验证错误,这个BUG是隐藏的最深的,现在来重点提一提这个BUG

BUG:模型验证错误····多重性与关系“········”中 Role“··············”中的引用约束冲突。因为 Dependent Role 中的所有属性都不可以为 null,Principal Role 的多重性必须为“1”。

里面值类型不能为空(如果没有初始化时为0),所以EF报错,你要么给外键加上Required标记指定它必须存在,要么给一个可为空的int型,像这个示例里面外键PersonID是必须的,然后有些对应是0-1 对 1,所以这时候就疑惑了我们怎么给外键赋值,我们有一种办法命名一种类型他的值可以int也可以为空,但是EF会认识我们这种独特的外键吗?还好EF早想到了这点,有一种泛型可以为空也可以为你想要的类型,这种就是Nullable<T> ,在这个方法中我们只要将外键PersonID的类型换成 这个

public Nullable<int> PersonID{get;set;}

自己本身与数据库类型的对应,C

还有一个比较常见的BUG吧,来提一提。

BUG:······: 引用约束的 Dependent Role 中所有属性的类型都必须与 Principal Role 中相应的属性类型相同。引用约束“·····”中,实体“····”的属性“····”的类型与实体“·····”的属性“·····”的类型不匹配。

这个bug就是相对应主体和外键不匹配的情况,相对应的类如下

    public Person{
    public long ID{get;set;}
    public string name{get;set;}
    public virtual SexInfo Sex{get;set;
                                            }


public SexInfo{
    public int ID{get;set;
    public bool Sex{get;set;
    public int PersonID{get;set;}
    public Person person{get;set;}
                            }

Person里面的主键我改成了long型,然而外键PersonID却是int型,出现这个错误是对外键的认识还不够,外键其实就是主键的“分身”,主键是long型,外键必须也是long型,同理主键是int型外键也必须是ing型,

ps:导航属性是指对象,比如说Person类实例person,而外键是指存贮在数据库里面的一个特殊的列名。


充分认识导航属性和外键是搭建一个扎实的数据库结构的基础,在学习和应用EF的过程中也是了解数据库的结构的学习过程,EF或许在运行速度方法上比一般的SQL语句要慢,但是用EF我们可以更加方便的搭建一个好的数据体系,搭建一个好的数据体系可以让你在完成项目的时候事半功倍。

基于python2

scrapy是一款非常轻量级的爬虫框架,但是由于它隐藏了太多关于网络请求的细节,所以我们有时候会遭遇到一下很尴尬的bug,当然这主要是因为碰到一些不规范的网站。

python的编码转码网上有很多文章,如果你不了解这个你可以参考下面了解。

Ned Batchelder 关于python unicode和str的理解,通俗易懂

关于scrapy 入门

关于 encode的认识

通过上面我们可以很好的理解python的转码译码,在这里我想谈一下我自己对其的认识吧,我一开始接触的c语言序列的基本上都是强类型,比如C里面假如我想写一个函数每个传人的参数都得是有类型的,但是python弱化了类型这一点,python也是面对对象的,但是他的对象就是鸡同鸭讲,照猫画虎就能运行,弱类型适合动态语言,我们不确定下一行代码输入的是什么,自从学python起,一直感觉python对类型一直不严格,这样就给了我一种错觉,只要长得差不多就能一样的比划,比如在两个string,'中国',u'中国',看起来差不多但是如果你把u'中国'存入文件中就会出错(假如你没定义编码规则)

UnicodeEncodeError: 'ascii' codec can't encode characters in position 344-351: ordinal not in range(128)

unicode字符编码错误,要想理解这个要对unicode字符集和unicode编码有一定的理解,推荐你读一下这篇博客字符编码的知识,python内部使用unicode字符集存贮所以的编码的字符,为什么要用unicode字符集举个栗子吧:

A是米国的程序员,他使用asicc编码的文件上传了一封邮件,
B是中国的程序员他使用gbk编码的文件上传了一封邮件,
现在C要用程序同时处理A和B的邮件,有两种解决方法他把A的文件译码再编码成B的gbk,或者将B的文件译码成asicc但是中文无法处理,那么只能使用第一种方法将A的文件编码成gbk,但是改天D又来啦,他是俄国人,天啊噜gbk可能没有把俄语编进去,那肿么办,我们迫切需要一种编码可以把所以的字符放进去,所以unicode出现了,Unicode中将字符集按照一定的类别划分到0~16这17个层面(Planes)中,每个层面中拥有216=65536个字符码,因此Unicode总共拥有的字符码,也即是Unicode的字符空间总共有17*65536=1114112,一共有1114112这么多的字符可以用,这下我们不用担心了吧,太好了这下不用愁了,

python 内部使用unicode字符集作为一个译码中转站,因为他编码了所以的字符集,只要你能在自己编码方案上找到自己的字,我就能在unicode字符集找到你的位置,所以使用unicode可以很好的解决多种编码方案产生的问题(比如gbk,utf-8)

当然其他编码方案如果想使用unicode解码成其他的必须同unicode有一一对应关系,不过现在主流的编码方案如gbk,gb2312,utf-8都是unicode系的。

了解了这些基础知识就可以知道了为什么存贮u'中国'存不进文件里面去了,因为unicode并不提供给当今字符解析器的方法,就是\u234e一个16进制数字,屏幕上不知道他对应什么图形,所以python系统要求存进文件的必须是字节流,也就是可以unicode是一种更高级的字符流,这个字符流能存贮当今世界所以定义的字符,但是他只是一个规定字符集合,我们只需要把发现的字符放进去占据一个位置,但是我们不需要考虑屏幕是否认识这个字符,这个字符的存贮由编码方案负责,如utf-8这些,假如没有字符编码方案可以存贮这些,我们虽然在unicode上有这个字符但是我们无法print出来,所以我们必须将unicode转换成普通字符流,有人就会问了,假如我真的没有找到一个合适编码方案可以存贮所有语言,我们可以将他编码成unicode—escape类型,这里我们不多讲。

这就可以解释我们大部分碰到的错误unicodedecodeerror和unicodeencodeerror错误,都是因为字符编码方案不了解造成的,网上很多说碰到这种错误就encode,decode搞一下就行但是不弄清楚这背后的知识就会犯迷糊。

接下来我谈谈我遇到的错误吧,在爬取http://yjsy.ncu.edu.cn/yjs_showmsg.asp?id=2770这个页面时(这是一个不规范的页面没有设置charset),因为每个spider调用的

response.xpath('//xpath').extract()    

选择器返回的是一个unicode编码的字符集,但是他是接受的是一个字符流,spider可能调用了
response.body.decode(response.encoding)进行转码,但是这个response.encoding有时候会判断错误,比如将我一个gbk编码的文件判断成cp1253,这个时候假如我把他解码成encode成其他编码方式的话,我们就会得到乱码,那怎么纠正呢,我们可以这样干
先将得到的列表中每个content取出来,然后使用content.encode(resonse.encoding)转码成原始字符流,现在你可以将它用正确的编码转换成unicode了

下面是我github上的关于这个scrapy的项目,在coding_pitch.py文件里面就是对于这个乱码的处理

南昌大学教务处公告爬取

GitHub推出一个对学生和教师的福利包,对于学生来说这是一个不小
的福利,只要通过一个edu邮箱就可以领取,但奈何国内有些无良人买卖
邮箱,所以GitHub对于.cn的邮箱一律拒绝,但是可以通过上传学生证的方法
得到验证,题主刚开始用学校邮箱试了试,失败了,抱着试一试的心态,上传了
学生证,没想到第二天就给我回复,并给我这个豪华大礼包,接下来我就介绍介绍
如何用这个包来.

有些人在网上说,上传学生证没有用,可能是那个plan(GitHub会叫你写一点你想用GitHub做什么)用的是中文写的,最好用英文写-_-,回复的会快一点.

Digital Ocean —VPS 50刀

以前貌似是100刀,现在缩水一半了,不知道为什么.

DigitalOcean是一家以优质的VPS服务器著名,毕竟用SSD做存贮的服务器商没几家.

这个是包小时的我们可以最便宜的5刀每月,提供20GSSD,1TB流量,我们可以用它来搭建服务器或者搭建一个shadowsocks服务器,安装shadowsocks很简单,但是怎么得到这50刀就要花点时间了.

你要是想得到这50刀必须先充值5刀,但是怎么给钱有是个问题,真是有钱也花出去啊.你可以选择绑卡,但是很复杂不一定能绑的上,最好的方式使用PayPal付这5刀,PayPal可以绑定银联卡付款,但是这个PayPal注册又是个问题,当时题主注册的时候一直提示服务器故障.

当时去上网搜了搜,中国大陆是有这个情况,可以通过贝宝(PayPal在中国的分公司)来注册

提醒一句,绑银联卡的时候最好用IE来绑定,别问我为什么谷歌浏览器不行—-

选择VPS的时候推荐San Francisco,延迟最低.

搭建shadowsocks可以参考这篇博客

NameCheap

ME域名一个(一年,价值8.99刀)PositiveSSL一个 (一年,价值9刀)

这个要想得到域名必须通过邮箱验证还好NameCheape承认.edu.cn邮箱

提醒一下,通过DVC验证的时候选择邮箱验证就够了.虽然不是你的邮箱但是会把资料发到你提供的邮箱

可以参考这篇博客搭建你的https网站,本站也是采用这种方法搭建的.但是有一点不同的时,现在NameCheap直接发给我一个.crt文件和.ca-bundle文件(用于Apache),所以把.key文件和.crt文件放到服务器上配置一下就好了.

GitHub Micro account 7刀/month

这个不错我们可以有五个私有项目,一直可以用到你毕业.

这个不错哦,妈妈再也不怕我写的stupid代码被人看到了 O(∩_∩)O哈哈~.

numpy简单来说就是python的C版数组实现,因为python原生列表虽然好使,但是生成大量数据时开销很大,而numpy是基于C的,生成大量数组非常简单,而且操作他们速度非常快.

由于numpy是基于C的,所以numpy是一种强类型的,当然numpy是可以判断数组里面是数据类型,但是我们可以显示声明他们,dtype是声明的参数,一般我们通过下面的方式简单声明一个narray

import numpy as np
arr = np.array([1, 2, 3], dtype=np.int32)

numpy还有一个强大的地方是多维数组,numpy对多维数组的支持很好.只要简单的使用嵌套序列就能被转化成多维数组.
比如
arr2 = np.array([[1, 2], [3, 4]])

numpy另一个强大的地方是矢量化,这对于科学计算来说非常有用

比如前面的arr,
我们可以简单使用

arr3 = arr * arr

得到另外一个序列 [1, 4, 9],数组间的运算应用到了元素级.

numpy之所以成为数据分析的基本数据结构,还在用取数据的灵活性

对于一维数组来说,python自身的列表就支持切片处理,numpy不仅支持切片处理,还支持列表取出,比如:

>>> num =  np.arange(10)
>>> num[[3, 1, 0]]
array([3, 1, 0])
在一维数组里面这个并没有什么优势,因为我们可以通过数据简单一个构造器[num[x] for x in [3, 1, 0]]构造出来.

当但在多维数组我们使用构造器非常繁琐了, numpy使用了很多技术使我们很方便的取出多维数组

我们先创建一个多维数组

arr = np.arange(32).reshape((8, 4))

生成的arr是

array([[ 0,  1,  2,  3],
   [ 4,  5,  6,  7],
   [ 8,  9, 10, 11],
   [12, 13, 14, 15],
   [16, 17, 18, 19],
   [20, 21, 22, 23],
   [24, 25, 26, 27],
   [28, 29, 30, 31]])

现在像一维数组一样的取出数据

In[13]: arr[[0, 3]]
Out[13]: 
array([[ 0,  1,  2,  3],
   [12, 13, 14, 15]])

我们取出了一个二维数组

试试用这个

In[14]: arr[arr > 8]
Out[14]: 
array([ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
   26, 27, 28, 29, 30, 31])

我们取出一个一维数组,这个arr > 8 是也是一个二维数组对应原来数组的每个位置都有一个布尔值代替,当numpy判断得到是一个array数组时会广播每个值来判断是否获取,这个可比你用for循环快了很多.

array([[False, False, False, False],
   [False, False, False, False],
   [False,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True],
   [ True,  True,  True,  True]], dtype=bool)
numpy很善于处理不同的选择,当你直接给一个数组时,如上面的arr[[0, 3, 4]],它默认第二维为全部选择也就是想当与arr[[0, 3, 4], :],当你给确定的值时,他就会在第二维上取响应的值,比如
arr[[0, 3, 4], 1] 或 arr[[0, 3, 4], [1, 1, 1]]

从上面你可以看到,如果第二维你每个都想取第二个,你可以直接写一个整数就行,numpy会广播过去,假如想你分别再第一维的每个上分别对应取哪个你就可以用数组来分别选择.

有时候我们想在二维数组上面取出一个矩形块,直接使用 arr[[0, 3, 4], [1, 2, 3]]只能取出二维数组矩形块的对角线,我们这时候就可以先取出第一维的矩形列,然后再在取出的列中取出矩形行
arr[[0, 3, 4]][:, [1, 2, 3]]

当然我们还可以用二维数组来取出数据
arr[[[0], [3], [4]], [[1, 2, 3]]]

[[0], [3], [4]]代表第一维的1, 4, 5列, [[1, 2, 3]]代表第二维的2, 3, 4行.

numpy给我们提供一个函数将一维数组转换成二维数组我们可以简单使用
np.ix_([0, 3, 4], [1, 2, 3])

生成二维数组,这样我们使用

arr[np.ix_([0, 3, 4], [1, 2, 3])]

就可以取出矩形块了.