opencv&Qt环境配置过程记录

我就找地记录下配置过程.

  1. 先下载opencv 官网http://opencv.org/

  2. 编译opencv需要cmake, so下载cmake吧 官网https://cmake.org

  3. 在Qt选项中--编译与运行cmake项中配置好cmake

  4. 用Qt打开项目opencv/source/CmakeLists.txt, 于是你会发现它哗啦啦生成一大堆东西.
    然后一般的教程就不往下说了, 于是就被坑了!!!
    好吧, 经过各种谷各种歌之后, 果然let's go!

  5. 生成完了后 用qt运行, 这个时候会调用mingW32-make来编译opencv

  6. 嗯最好加一下mingW32-make的环境变量.

  7. 编译好了后 进入qt刚刚生成的build-xxxxx的目录, 在项目中可以配置该目录

  8. 用 mingW32-make install 很快的就会安装好 安装好后文件在当前目录下的install文件夹内.

  9. 我们需要用到的就是 opencv源码包里 build/include (里面有opencv和opencv2文件夹)
    mingW32-make install命令生成的 install文件夹
    添加install文件夹下的xx/bin到系统path下。

  10. [开始使用opencv]

  11. 用qt创建一个新的控制台应用

  12. 修改pro文件的内容, 加入

    INCLUDEPATH += \
        D:/OpenCV/include \
        D:/OpenCV/include/opencv/ \
        D:/OpenCV/include/opencv2/
    
    LIBS += \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_calib3d310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_core310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_features2d310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_flann310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_highgui310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_imgcodecs310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_imgproc310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_ml310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_objdetect310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_photo310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_shape310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_stitching310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_superres310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_ts310.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_video310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_videoio310.dll.a \
        D:/OpenCV/mingw/x86/mingw/lib/libopencv_videostab310.dll.a
    
    //当然D盘什么的地址是我当前的盘符地址, 记得改一下嗷, 还有libs中 x.dll.a文件由于opencv版本不一样, 可能有差别
    
  13. 在main.cpp中添加一个测试代码吧

    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    
    using namespace cv;
    
    int main()
    {
        //声明IplImage指针
        IplImage*pImg;
        //载入图片
        pImg=cvLoadImage("e:/a.jpg",1);
        //创建窗口
        cvNamedWindow("my",1);
        //显示图像
        cvShowImage("my",pImg);
        //等待按键
        cvWaitKey(0);
        //销毁窗口
        cvDestroyWindow("my");
        //释放图像
        cvReleaseImage(&pImg);
        return 0;
    }
    

能运行就说明安装ok啦, 嗯, 我去玩opencv去了~

实现一个竖向滚轮吧

嗯,其实在qt里有滚轮的控件,叫 ScrollBar ,在 Qt.labs.controls 1.0 包里,好吧,我就是闲的慌,想自己实现一个它的效果。=。=说起来汗颜的是,一开始看 Documentation 的时候,看到这个控件的几个属性,还各种的满不在乎,觉得尼玛这个设计好愚蠢。但当我实现了这个控件时,才发现,尼玛,我自己竟然也把这几个属性留出接口了,这真是,好尴尬啊……打脸啊……


分析下它的功能吧,仔细把玩了下竖向滚轮。

  • 滚轮的中间有一个滑块
  • 滑块占整体滚轮的比例跟文本区的可显示大小/整体文本大小有关
  • 滑块的显示高度跟当前可见文本区域在整个文本区域的位置有关
  • 滑块可以拖动的,拖动的时候会跟随鼠标的拖动方向移动
  • 滑块拖动的时候,它控制的文本也会同方向移动
  • 点击滑块上面的空白,页面会随着向上滚动,直至滑块到达鼠标位置
  • 点击滑块下面的空白,页面会随着向下滚动,直至滑块到达鼠标位置

其实,从描述上可以看到,如果就是简单的拿 Rectangle 堆出一个滚轮的话,它是一个三层结构。

三层结构大概的示意图

一开始我的第一反应是在中间的滑块上单独再有一个 MouseArea ,不过后来仔细琢磨了下发现其实那样设计会造成移动滑块的时候的处理变得更加复杂,其实那样设计除了看着舒心,并没有带来什么好处。所以,权衡之后,还是打算只用一个大 MouseArea 盖在上面,通过对鼠标坐标移动的信号来对不同的事件进行处理。

其实想通的话,整个滚轮实现起来并不难。不过有两个坑的地方,一个是按空白的地方,页面自动跑那里,我是靠了一个定时器来实现的页面滚动,通过定时页面高度自增自减。还一个地方其实就是滚轮移动比例到页面移动比例的换算千万别弄忘了,(。・`ω´・) 我一开始就忘记了,结果整个页面起飞了。


嗯,剩下这个控件很简单了,我依然将控件的逻辑部分和样式进行了分离,分别在 control 和 style 里,同时 control 里为了能够控制页面滚动,所以留了一个 Item 类型的属性,如果没对这个属性进行设置,会发现滚轮没有效果,但是不会报错的!

最终的大概效果。
控件的样子

代码如下:

control文件

//VerticalScrollBar.qml
import QtQuick 2.5
import QtQuick.Controls 1.3
import QtQuick.Controls.Private 1.0

Control {
    id: root
    implicitWidth: 10
    implicitHeight: parent.height

    style: Qt.createComponent("VerticalScrollBarStyle.qml", root)

    property Item handleItem // verticalscrollbar 控制的Flickable类控件,必须要设置哦,不设置滚轮是没有反应的
    property alias size: scrollbtn.height //滚轮的长度 height
    property alias position: scrollbtn.y //滚轮的高度 y

    property int __scrollState: 0 //-1向上滚 0不滚 1向下滚
    property int __scrollStep: 20 //按下空白时候的移动步长
    property double __offset: 0
    property bool __dragScroll: false //是否按下滚轮

    clip: true

    Timer {
        interval: 20
        running: !!handleItem
             &&__scrollState!==0
             && handleItem.contentY>=0
             && handleItem.contentY<=handleItem.contentHeight-handleItem.height
        repeat: true
        onTriggered: {
            if (!!handleItem) {
                handleItem.contentY += __scrollStep*__scrollState
            }
        }
    }

    Loader {
        id: backboard
        property Component __bkract //背景图案
        anchors.fill: parent
        sourceComponent: __style && __style.background ? __style.background : __bkract
    }

    Loader {
        id: scrollbtn
        property Component __scrollbtn //滑轮图案
        width: parent.width
        sourceComponent: __style && __style.scroll ? __style.scroll : __scrollbtn

        onYChanged: {
            if (y<mousearea.mouseY && y>mousearea.mouseY-height) {
                __scrollState = 0
            }
        }
    }

    MouseArea {
        id: mousearea
        anchors.fill: backboard

        property double oldY: -1
        onPressed: {
            if(mouseY<=scrollbtn.y) {//按住滚轮空白区域滚轮往上
                __scrollState = -1

            }else if(mouseY>=scrollbtn.y+scrollbtn.height) {//按住滚轮空白区域滚轮往下
                __scrollState = 1

            }else {
                __scrollState = 0
                __dragScroll = true
                oldY = mouseY

                __offset = !!handleItem? handleItem.contentY-oldY*handleItem.contentHeight/handleItem.height : 0
            }
        }
        onReleased: {
            __scrollState = 0
            __dragScroll = false
            oldY = -1
        }
        onPositionChanged: {
            if(!!handleItem && pressed && oldY!==-1) {
                var offsetMouseY = mouseY*handleItem.contentHeight/handleItem.height
                handleItem.contentY = __offset+offsetMouseY>=0? (__offset+offsetMouseY<=handleItem.contentHeight-handleItem.height? __offset+offsetMouseY : handleItem.contentHeight-handleItem.height) : 0
            }
        }
    }
}

style文件

//VerticalScrollBarStyle.qml
import QtQuick 2.5
import QtQuick.Controls 1.3
import QtQuick.Controls.Private 1.0

Style {
    id:style

    //背景样式
    property Component background: Rectangle {
        color: "#fcfcfc"
        Rectangle {
            width: 1
            height: control.height
            color: "#cccccc"
        }
    }

    //滚动条滚轮
    property Component scroll: Item {
        Rectangle {
            width: parent.width-4
            height: parent.height-4
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.horizontalCenterOffset: 0.5
            anchors.verticalCenter: parent.verticalCenter
            color: control.__dragScroll? "#777777" : "#bbbbbb"
            radius: width
        }
    }
}

嗯,就是酱紫简单就哦了,具体使用例子,请烦劳移驾github吧~[(๑→ܫ←)github传送门]

来,unity5手动激活

结局:今天折腾了一天,终于把unity成功安装激活好了。

起因:最近打算自学下unity,然后今天兴冲冲的跑去u3d官网看了老久,并打算装个玩玩~
然后我下了最新的unity5,一路顺利,当时愚蠢,没下离线安装包,然后在线装了好久好久……
不过还好,装的还是很快的,于是装完后,我兴冲冲的点开unity。
然后,弹出了激活页面。
穷人嘛,就打算装个免费的personal版本就好。
填好了用户名、密码、喜好目的等等,终于到最后看到“start using unity”。
大大的页面上一行“You'er done!”看的人真是热血沸腾,然后我就点了开始的按钮,然后,然后……程序的就真的“done”了……
||( TAT)||……摔!

于是,我开始用伟大的度娘试图解决这个问题。

有人说是,激活服务器时钟慢多等几小时就好;有人说要多试几次就能激活;有人说遇见这种情况就重新注册就行了;也有人说是激活服务器被墙了,上个vpn激活……
我大体都试了一遍,简直是泪流满面……
就在我打算实在不行上D版吧的时候,发现嘿嘿,其实还可以手动激活~
真是柳暗花明。


手动激活教程如下,本来想自己写的,当时太激动,完全忘记截图了。

源地址:百度贴吧

  1. 激活第一步,断网,双击unity3d的桌面图标这时会出现这样的页面,点击红框内的按钮。

    1

  2. 然后会出现如下页面,点击红框内的按钮,随便找个文件夹保存就行。

    2

  3. 点击如步骤2给的链接https://license.unity3d.com/manual会出现如下窗口,点击红框内的按钮上传你刚才保存的文件。点击“ok”按钮。

    3

  4. 步骤3完成后会出现如下页面,选择第二个内容,点击“ok”按钮

    4

  5. 之后输入你在官网上注册的用户名和密码就出现如下页面,点击蓝色按钮,下载证书。

    5

  6. 这时需要重新断网,再次打开unity3d,点击红框内的按钮,上传你第5步下载的证书即可,马上你就能看到心仪的unity3d了。

    6

对于又可以换肤,每种皮肤下又可换多种颜色的一种qml实现策略

好吧,其实这个是之前做的项目中遇到的一个小问题。当时的情况是这样的,我们一直用qml开发app,然后头头希望我们的程序能够有换肤功能,并且在每种皮肤下,都可以替换不同颜色。也就是标题所说的“又可以换肤,每种皮肤下又可换多种颜色”。

嗯,分析下要实现的需求。

换皮肤的话,那就要求在制作页面+组件的时候要小心,尤其是组件的时候,将逻辑功能control和显示样式style进行拆分。然后把style统一放在某个文件夹下,通过一个变量存储style文件夹的地址,然后换肤的过程就是替换style文件夹地址的过程。嗯,类似如下:

项目结构

图中嘛,皮肤1和皮肤2文件夹内都有一套对应组件的style文件。然后替换不同的地址,造成了组件样式的变化。同时,因为把所有逻辑功能等放在了control文件里,所以样式style文件中,只有外观的定义修改起来也相对简单,并且接口什么的不会因为样式的改变而改变。

再说说换颜色,这个很好实现,我们原来在编写代码的时候,颜色值都是硬编码在代码中的,那么只要把之前用到的颜色值进行归类,然后替换成全局的颜色变量就好了。

不过现在的问题就是,将两者组合起来后,因为不同的皮肤,对于颜色的多少啊,位置啥的定义都不相同,所以提取出来的颜色该以什么结构放在项目中。为了解决这个问题,我在每个皮肤文件夹内都有一个调色板文件,然后在全局有个取色器文件。然后根据当前所选的皮肤来进行取色。嗯,大概的结构如下:

带调色板的项目结构

大概就是这样的一个结构,不过在具体实现的时候当然没有就这样把取色器代码放在根上,这样多乱啊=3=

代码如下:

//ColorPalette.qml 即上文所说的取色器
pragma Singleton
import QtQuick 2.4

Item {
    id:colorRoot
    
    property string __colorType: "blue" //主题颜色
    
    property string __colorComponentUrl: "."+"/ColorTable.qml" //主题URL
    
    property Component __colorComponent: null
    
    property var __colorTable
    
    function initComponent() {
        if (__colorComponent === null) {
            colorRoot.__colorComponent = Qt.createComponent(colorRoot.__colorComponentUrl)
            if (colorRoot.__colorComponent.status === Component.Ready) {
                colorRoot.__colorTable = colorRoot.__colorComponent.createObject(colorRoot);
            }
        }
    }

    function color(type) {
        colorRoot.initComponent()
        try {
            if (colorRoot.__colorTable[colorRoot.__colorType].hasOwnProperty(type)){
                return colorRoot.__colorTable[colorRoot.__colorType][type]
            }else{
                return "#000000"
            }
        }catch (err) {
            return "#000000"
        }
    }
}

下面是每个皮肤里的调色板示例:

//ColorTable.qml 即调色板
import QtQuick 2.4

QtObject {

    readonly property var blue: {
        1:"#cee9f9",
        2:"#90bcd6",
        3:"#c0d0df",
        4:"#ffffff",
        5:"#57a2d8",
        6:"#007aff",
        7:"#aae0ff",
        8:"#227bdd",
        9:"#3a6ca9",
        10:"#7099bf",
    }

    readonly property var red: {
        1:"#ffe3e3",
        2:"#ffa3a3",
        3:"#f5d0d1",
        4:"#ffffff",
        5:"#57a2d8",
        6:"#007aff",
        7:"#aae0ff",
        8:"#227bdd",
        9:"#ffbcc0",
        10:"#eb7777",
    }
}

可以看到在这个皮肤的调色板中,一共定义了2种色调,一个叫做blue,一个叫做red,然后每种色调里面都用到了10种颜色。假设在组件里的某个颜色你需要用9号颜色,可以类似ColorPalette.color(9)这么写。

最后要注意的事,由于当时项目不要求有动态换肤,或者说是用户自己在程序里换肤这个功能,所以你看到在上面的ColorPalette.qml代码中,创建调色板的Component只有一次,而没有监控__colorComponentUrl里的主题url的变化,当它变化的时候重新createComponent

嗯,酱油结束,继续干活去,嘿!( ̄▽ ̄)"

用qml仿照ios开关按钮效果

新年过后刚上班,略感无聊,然后手里玩着苹果的测试机,突然觉得苹果的开关按钮效果很惹人,遂打算实现一个。

苹果开关按钮效果

我随便从网上找了一个苹果按钮的gif图,嗯,这个就是我想要实现的效果。

首先,从外观效果来分析一下吧。

  • 底板是椭圆的,并且左右两端是半个圆。
  • 按钮是圆形的。
  • 关闭状态(按钮在左侧)的时候,底板是白色的,并有灰色的边框。
  • 打开状态(按钮在右侧)的时候,底板是绿色的,看不出有没有边框。
  • 在关闭状态,按下按钮或拖动按钮到右侧,变为打开状态。
  • 在打开状态,按下按钮或拖动按钮到左侧,变为关闭状态。
  • 按下按钮不释放(pressed && !release)的时候,按钮会横向变肥一下,释放后,变回最初的宽度。
  • 按下按钮不释放的时候,底板的椭圆会从边框按照边缘的椭圆形慢慢吞噬成灰色。
  • 如果边按下不释放,边快速拖动按钮,被拖动过的左侧区域不会显示吞噬的效果。
  • 按钮的圆形外侧有阴影。

大概观察了下,按钮好像差不多就这些属性。

恩,QtQuick 2.4里,我记得好像是已经新添加了一个类似开关按钮的控件,本来是打算用那个控件重新设计style实现,不过后来我找了半天也没找到那个控件,忘记叫啥名字了。(已经记起名字叫做Switch。)于是,最后决定还是自己完全重新用Rectangle画一个开关按钮粗来。

根据上面分析的结果,可以这样设计这个组件:整个组件分为3层,底下的背景层、上面的圆按钮、还有在背景层和按钮之间要有一层遮照层。背景、和按钮很好理解,至于为什么还要有一层遮照层呢,主要是因为按下按钮并快速拖动的时候,那个背景吞噬的效果并不是所有控件覆盖的,而是只是在未被拖过的背景才显示,=。=我是实在没啥好的解决办法了,所以才在加上了一层遮照层。

恩,感觉整个设计里面没有啥太大的难度,哦,就是有一点,不是在按下的过程中有吞噬的效果么,本来想可能需要再放一个Rectangle实现。后来发现,其实我可以用背景矩形的边框做文章。于是,当按钮被按下的时候,背景矩形的边框越來越大越来越大,然后吞噬了整个面板~~嘿嘿

同样,在实现的时候,我还是将控件分为了control和style部分,所有行为的处理都在control里,而样式效果放在了style里。

代码如下:

这是control的代码

//RadioButton.qml

import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0

Control {
    id:root

    implicitWidth: 140
    implicitHeight: 80

    style: Qt.createComponent("RadioButtonStyle.qml", root)

    /*! 是否被选中 */
    property bool checked: false
    /*! 是否被按下,按下并不一定选中 */
    readonly property bool pressed: mousearea.pressed || upperma.pressed

    MouseArea {
        id: mousearea
        anchors.fill: parent
        onClicked: root.checked = !root.checked
    }

    Loader {
        id: maskloader
        property Component __mask //上层按钮
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.leftMargin: __style && __style.bkBorderWidth ? __style.bkBorderWidth : 0
        anchors.rightMargin: parent.width-upperloader.x-upperloader.width
        anchors.verticalCenter: parent.verticalCenter
        sourceComponent: __style && __style.mask ? __style.mask : __mask
    }

    Loader {
        id: upperloader
        property Component __up //上层按钮
        property real bkBorderWidth: __style && __style.bkBorderWidth ? __style.bkBorderWidth : 0
        anchors.verticalCenter: parent.verticalCenter
        sourceComponent: __style && __style.upper ? __style.upper : __up
        x: root.checked ? root.width-width-bkBorderWidth : bkBorderWidth

        onXChanged: {
            if (x===root.width-width-bkBorderWidth) {
                root.checked = true
            }else if (x===bkBorderWidth) {
                root.checked = false
            }
        }

        MouseArea {
            id:upperma
            anchors.fill: parent
            drag.target: parent
            drag.axis: Drag.XAxis
            drag.maximumX: root.width-upperloader.width-upperloader.bkBorderWidth
            drag.minimumX: upperloader.bkBorderWidth
            onClicked: root.checked = !root.checked
        }
    }
}

style的代码

//RadioButtonStyle.qml

import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0


Style {
    id:style
    property real bkBorderWidth: 2 //背景边框宽度
    property real btnBorderWidth: 1 //按钮边框宽度

    property color borderColor: "#B4B4B4" //边框颜色
    property color checkedColor: "#4BD962" //选中颜色
    property color defaultColor: "#FFFFFF" //未选中颜色

    property Component upper: Rectangle {
        height: control.height-style.bkBorderWidth*2
        width: control.pressed ? 4/3*height : height
        radius: height/2
        color: style.defaultColor
        border.width: style.btnBorderWidth
        border.color: style.borderColor

        Behavior on width {
            PropertyAnimation {
            }
        }

    }

    property Component mask: Rectangle {
        height: control.height-style.bkBorderWidth*2
        width: parent.width
        radius: height/2
        color: control.checked ? style.checkedColor : style.borderColor
    }

    property Component background: Rectangle {
        height: control.height
        width: control.width
        radius: height/2
        color: style.defaultColor
        border.width: control.checked || (control.pressed && !control.checked) ? height : style.bkBorderWidth
        border.color: control.checked ? style.checkedColor : style.borderColor

        Behavior on border.width {
            PropertyAnimation {
                duration: 350
            }
        }
    }

    //布局面板
    property Component panel: Item {
        anchors.fill: parent

        Loader {
            anchors.centerIn: parent
            sourceComponent: background
        }

    }
}

运行截图:

未选:

未选

选中:

选中

按下的过程中:

按下的过程

最后,我有两个效果没有实现,被我放弃了。

  • 按钮圆圈的阴影,我没有加,我试着加了DropShadow,结果整个圈的边缘都看着像狗啃的一样,所以删掉了。
  • 在按钮滑块自动移动的时候,我没有加动画效果,原因是加了效果后,如果再按下按钮而又不释放的话,会看到奇异的按钮慢慢滑动的效果。原因是,我目前没有太好的办法区分按下造成的滑块移动和点击造成的滑块移动的区别。

QAQ谁有好方法,可以教教我~

FarBox: Simple is Everything

首先,我要说,这不是一篇软文。作为一个苦逼的程序员,我总是会对各种各样的技术与产品感兴趣,愿意尝试其中。在这之前,我也仅仅试用一下,然后内心感叹一下。当某一天,我正跟别人交流说xxx好时候,经理突然问我,"你为何觉得xxx好呢,它究竟好在哪"时,一瞬间,我竟无言以对,仿佛所有的描述都无法真正的印证它好。时间推移,我记住的仅仅是它好,而遗忘了为何好。于是,从现在起,我会尽量记下,对于那些让我忽然感动的瞬间,以及它们为何感动我。

我为什么会突然在意起FarBox

我总是喜欢在网上看一些大牛的文章,一次偶然的机会,我点进了,Hepo的博客之中。我该怎么形容第一次见面的感觉呢,白、纯净、震撼。

farbox blog

这种极致的白,瞬间吸引了我。在我浏览完整个页面后,我点进去了主页上唯一一篇文章《十二月的十二月里,谈谈Python与创业》。我一直非常狂热的热爱着python,于是这样一篇标题的文章,我对它完全没有抵抗力。其实,到目前为止,我并没有对FarBox有任何兴趣,每天出现在互联网上各种各样的blog工具太多了。在文中,提到了PyCon的杭州站与Hepo自己的《FarBox & Python》,不出意外,我看了下Hepo的那篇技术分享。

why i'm interested in farbox

这才是真正直戳我心灵的地方!

代码,是艺术的一部分。

该怎么样形容这种感受呢,我一直认为,代码,不仅仅是为了实现程序的功能;好的代码、它自己的结构、变量命名啊,等等啥的,就像是一首好听的歌,跌宕起伏。我一直试图像别人描绘这种感觉,却找不到一种合适的语言来表达。而这一刻、这一句话,我觉得完全足够描绘我内心中对于代码的所有幻想。就是它了!这瞬间,我竟然有些热泪盈眶。

感动过后,我开始关注Hepo和他的产品FarBox来,有着这样想法的一个人,他的产品又会是什么样呢。

FarBox

有一句话我很喜欢。

是,现在想写东西的人实在有太多可选择的博客平台了
——多到我们终于什么东西也没写成。

FarBox的主页,跟它的理念一样,就是简单。干净到仿佛一切都不存在,除了文字。

farbox index

FarBox是什么,在我看来,它是一个博客平台。或者说,一个云服务+动态md翻译器+网页托管(也许,我的描述不准确),当然还有一些灰常酷酷的功能。怀着试一试的想法,我注册了FarBox。

嗯,这里要吐槽的是,整个界面全是E文,我试图在其上找到任何sign up或者register的地方,但是就是没找到。于是,我万念俱灰的点向了sign-in。好吧,然后邮箱登录的地方看到了有一个提示,如果是第一次输入邮箱就是注册( ̄▽ ̄),嗯,你赢了。

登录后,进入到了,自己home页,依然是白,大面积的白。

farbox home

这个页面的布局,我觉得并不好看,或者说是为了追求一种简单,而造成了空间的浪费。我的屏幕辣么大,你就放6个图标,底下还有一屏的内容(-。-;,滚屏好累的你造么。好吧,终归是没有全部都是E文,我先去我的网站创建了一个站子。

add new site

嘿,一定要说说,这个弹出提示我好喜欢:-),辣种很纯净的感觉,我就不想吐槽工作时候做的那个弹出框了,真的莫名的提不起爱来,好吧。嗯,我尝试过了,这个弹出框在遮罩层上并不能移动,它始终保持在center-in整个窗体中,如果父窗体过小,(((o(゚▽゚)o)))它会被腰斩看不见哦。不过我觉得,也只有我闲的没事回去开好小的窗体这么干了。

site setting

添加完后的网站会出现在你的sites列表中,上面的四个按钮分别是Dash Board, Statistics, Template Editor, Text Editor。

在体验一个博客产品时,除了一般的使用感觉外,有两点是我很在意的地方,一个是设置页,一个是其他用户用户的使用它做的站子。设置页的设置,能够看出它的定位,就是说,它是主要是为了做什么和为了哪种用户存在的。而他人的站子,可以让我很直观快速的先对这个产品能做到怎样的极致有个大致的判断。(≧∇≦)我是个很懒的人啊,能不做就尽量少做~

setting

FarBox的设置页很简单,简单到几乎没有什么功能了,对比wp的厚重的后台页。可以设置网站的基本信息、模版、每页显示内容数、第三方评论、黑名单、访问密码、域名等,0.0好像没啥可等了。这种简单的感觉让我觉得很清新、很喜欢,这也是当时为啥抛弃wp转向了te的原因之一。这里有一点我很喜欢,我发现拖拽图片自动可以生成图片地址,这简直是太帅太方便啦。

关于模版,FarBox可以自己编译模版,我大概的看了眼模版&api,语法貌似跟web.py的模版很像,嗯,我就是随便看看,要是不像就是我错了(=。=)。

哦,还有一点,现在的FarBox已经转变为收费服务啦,新用户可以免费试用30天,具体的收费价格的话,我瞅瞅:

Basic $1/year Senior $39/year Pro $99/year
文件数 300 3,000 Unlimited
月流量 100MB 20GB 100GB
$5/per-5years $39/yearly $99/yearly

感觉,如果让我再说说FarBox,我也说不出什么了,作为一个博客,除此之外,剩下的只有文字了。它提供了一张白纸,等着你落笔。

博客之外

我觉得FarBox最帅的地方在于,它用从Dropbox读取的文件夹结构来作为网站的结构。于是,我就在想,用户轻松在后台进行的删文件夹、添加文件夹、移动等等的操作,对于FarBox后台是多大的劳累啊,(°_°)我脑海中瞬间浮现了勤劳的粉刷匠。越是简单的表象,后台逻辑越是复杂。所以对于是动态网站的FarBox来说,这个设计想法是我觉得最牛气的创造了。

最后我一定要吐槽的

  • 整个界面,中/英文混杂在一起,尤其是登录页、系统邮件啥的完全没有一点中文,我并不讨厌这种设计,但是如果你是全英文的我也能理解,既然已经是混合的状态,为何不出一个中/英文切换来,要么纯英文、要么纯中文,这样biger才高呀~

  • FarBox是基于Dropbox的网站托管,那又高又大的城墙啊,简直是太不方便了,而且由于好像FarBox自己的服务器也在国外,有的时候,国内访问速度并不好啊。

  • 这真的是一款很酷的产品。但是如果我有一天打算放弃te,我还是会选择:git+markdown+一款静态网页生成器,我想我更想要的是最大限制的自由和折腾的乐趣。

更多阅读

额,js深复制

好吧,最近做页面的时候被js深深的坑到了。

当我轻松的用var a = b的时候,由于b是一个对象,并且在这之后我对b进行了修改,然后自然而然的悲剧了,a也一起改变了。虽然,我很快的想到了这是由于我用了浅复制造成了。于是,我开始百度深复制的方法。
b是一个array所以我自然而然的用了var a = b.slice(0)。然后我满怀信心的run了我的程序,然后又一次悲剧了QAQ

我意识到,由于我b中的每个元素,仍有可能是一个对象,造成其实它里面每个对象元素还是浅复制。(๑•́ ₃ •̀)

嗯,so,我不得不用递归来复制元素了,然后我懒得写了,就抄袭了一个[代码抄袭自],js你赢了(= =)

function deepCopy(srcObject) {
    var newObject
    if (srcObject instanceof Array) {
        newObject = []
        for (var i = 0; i < srcObject.length; i++) {
            newObject[i] = deepCopy(srcObject[i])
        }
        return newObject
    }else if (srcObject instanceof Object) {
        newObject = {}
        for (var j in srcObject) {
            newObject[j] = deepCopy(srcObject[j])
        }
        return newObject
    }else {
        return srcObject
    }
}

这是一个对比的例子~(我是在qml中用的js)

function test() {
    var a = ['1',{"aaa":"qqq"},'3']
    var b = deepCopy(a)
    var c = a.slice(0)
    a[1]["12"]="444"
    console.log(JSON.stringify(a[1]))
    console.log(JSON.stringify(b[1]))
    console.log(JSON.stringify(c[1]))
}

输出

qml: {"12":"444","aaa":"qqq"}
qml: {"aaa":"qqq"}
qml: {"12":"444","aaa":"qqq"}

qml实现QQ列表界面侧划效果

今天,出于需求,需要在列表中用qt实现一个类似qml实现QQ列表界面侧划效果。经过我不懈打酱油+听音乐+发呆+刷新闻后终于得以实现,记录下~

对于这个神奇的控件,就称为DragBar,我所定义它的行为为:

DragBar

  1. 它有[打开]和[关闭]两种状态。
  2. 在[关闭]状态时,点击或者是拖动其划过超过某一个阀值距离后,进入[打开]状态。
  3. 在[打开]状态时,点击或者是拖动任意距离,都会进入[关闭]状态。
  4. 在[打开]状态时,会出现两个按钮,一个为[修改]一个为[删除]。
  5. 当用DragBar组成列表后,同一时间只有一个DragBar为[打开]状态。

因为是有拖动组件然后出现按钮这样的行为,所以我在实现时,利用MouseArea的drag属性,将两层组件覆盖在一起,于是如果你拖动上层组件,就自然而然会出现下层组件的外形。

drag provides a convenient way to make an item draggable.

  • drag.target specifies the id of the item to drag.
  • drag.active specifies if the target item is currently being dragged.
  • drag.axis specifies whether dragging can be done horizontally (Drag.XAxis), vertically (Drag.YAxis), or both (Drag.XAndYAxis)
  • drag.minimum and drag.maximum limit how far the target can be dragged along the corresponding axes.

这是Qt文档中对于drag属性的描述,所以可以很轻松的做如下设计。

DragBar结构

由于在对DragBar的定义上,又要响应点击又要响应拖拽,所以我把事件放在了MouseArea的released事件上,并通过判断触发该事件时的x坐标来区分这几种行为。

[关闭状态]

  • x<=阀值
    上层关闭
  • x>阀值
    上层打开
  • 如果是点击,即press和release时x坐标未改变
    上层打开

[打开状态]

  • 点击关闭

在定义第5条中'当用DragBar组成列表后,同一时间只有一个DragBar为[打开]状态',所以我将DragBar的打开关闭状态与activefocus属性关联,当activefocus为false时,即当焦点不在该控件上时,DragBar关闭。

好像差不多就这样,具体代码如下:

//DragBar.qml
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0

Control {
    id:root

    implicitWidth: parent.width
    implicitHeight: 80

    style: Qt.createComponent("DragBarStyle.qml", root)

    property real boundary: 40 //滑动开关的边界值
    property alias isOpen: dragRec.isOpen //是否打开标志 true打开 false关闭

    onActiveFocusChanged: {
        if (!activeFocus) {
            isOpen = false
        }
    }

    Loader {
        id:backboard
        property Component __bkButtons //背景图标
        height: parent.height
        anchors.right: parent.right
        sourceComponent: __style && __style.buttons ? __style.buttons : __bkButtons
    }

    Loader {
        id: dragRec
        property Component __upPanel //上层面板
        property bool isOpen: false //是否打开标志 true打开 false关闭
        height: parent.height
        width: parent.width
        Drag.active: dragArea.drag.active
        sourceComponent: __style && __style.upper ? __style.upper : __upPanel

        onIsOpenChanged: {
            if (isOpen) {
                dropOpen.start()
            }else {
                dropClose.start()
            }
        }

        PropertyAnimation on x {
            id:dropOpen
            running:false
            to:dragArea.drag.minimumX
            easing.type: Easing.InOutQuad
        }

        PropertyAnimation on x {
            id:dropClose
            running:false
            to:dragArea.drag.maximumX
            easing.type: Easing.InOutQuad
        }

        MouseArea {
            id: dragArea
            anchors.fill: parent
            drag.target: parent
            drag.axis: Drag.XAxis
            drag.maximumX: 0
            drag.minimumX: -backboard.width
            property real oldx

            onPressed: {
                oldx = mouse.x
            }

            onReleased: {
                root.forceActiveFocus()
                console.log("dragRec.x:"+dragRec.x)
                if (!dragRec.isOpen) {
                    if (dragRec.x !== 0 && dragRec.x>-root.boundary) {
                        dropClose.start()
                    }else if (dragRec.x !== 0 || oldx===mouse.x){
                        dragRec.isOpen = true
                    }
                }else {
                    dragRec.isOpen = false
                }
            }
        }
    }

    function modifyfun(){
        console.log("修改操作")
    }

    function deletefun() {
        console.log("删除操作")
    }
}

这是对应的style样式的qml:

//DragBarStyle.qml
import QtQuick 2.4
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0

Style {
    id:style

    //背景上的按钮们
    property Component buttons: Row {
        height: parent.height

        Rectangle {
            height: parent.height
            width: 80
            color: "#f9cdac"

            Text{
                anchors.centerIn: parent
                text:"修改"
                color: "#ffffff"
            }

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    control.modifyfun()
                }
            }
        }

        Rectangle {
            id:aaaa
            height: parent.height
            width: 80
            color: "#fc9d9a"

            Text{
                anchors.centerIn: parent
                text:"删除"
                color: "#ffffff"
            }

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    control.deletefun()
                }
            }
        }
    }

    //上层面板样式
    property Component upper: Rectangle {
        anchors.fill: parent
        color: "#ffffff"
        Text {
            anchors.centerIn: parent
            text:"点一下或者拖拽就会打开"
            color: "#666666"
        }
    }

    //布局面板 可以设置底板背景颜色 不过一般看不见吧
    property Component panel: Item {
        anchors.fill: parent
    }
}

运行效果图:

~ 这个是全关闭的状态
关闭状态

~ 打开上面的DragBar
打开1

~ 打开下面的DragBar,上面的自动关闭
打开2

python题目收集

  • Python是如何进行内存管理的?

    小块空间内存池和大对象缓冲池

    参考:python内存管理

  • 什么是lambda函数?它有什么好处?

    Python支持一种有趣的语法,它允许你快速定义单行的最小函数。这些叫做 lambda 的函数是从Lisp中借用来的,可以被用在任何需要函数的地方。

     >>> def f(x):
     ...     return x*2
     ...     
     >>> f(3)
     6
    
     >>> g = lambda x: x*2
     >>> g(3)
     6
    
     >>> (lambda x: x*2)(3)
     6
    

    这是一个通常的函数声明,尽管以前你可能没有看到过定义在交互式窗口中的函数。这个 ... 说明它是一个多行的交互语句。只要在第一行的后面敲入回车,Python IDE会让你接着输入命令。

    这是一个 lambda 函数,它完成同上面普通函数相同的事情。注意这里的简短的语法;没有小括号, return 是默认的,并且函数没有名字,只有将它赋值给变量的变量名。

    你甚至可以不将 lambda 函数赋值给一个变量而使用它。这不是举世无双的东西,它只是展示了 lambda 函数只是一个内联函数。

    总之, lambda 函数是一个可以接收任意多个参数(包括可选参数)并且返回单个表达式值的函数。 lambda 函数不能包含命令,它们所包含的表达式不能超过一个。不要试图向 lambda 函数中塞入太多的东西;如果你需要更复杂的东西,应该定义一个普通函数,然后想让它多长就多长。

    参考:lambda

  • 如何反序的迭代一个序列?how do I iterate over a sequence in reverse order

    如果是一个list, 最快的解决方案是:

     list.reverse()
     try:
         for x in list:
             “do something with x”
     finally:
         list.reverse()
    

    如果不是list, 最通用但是稍慢的解决方案是:

     for x in sequence[::-1]:
         <do something with x>
    

    参考:反序遍历

  • Python是如何进行类型转换的?

     函数                      描述
     int(x [,base ])         将x转换为一个整数
     long(x [,base ])        将x转换为一个长整数
     float(x )               将x转换到一个浮点数
     complex(real [,imag ])  创建一个复数
     str(x )                 将对象 x 转换为字符串
     repr(x )                将对象 x 转换为表达式字符串
     eval(str )              用来计算在字符串中的有效Python表达式,并返回一个对象
     tuple(s )               将序列 s 转换为一个元组
     list(s )                将序列 s 转换为一个列表
     chr(x )                 将一个整数转换为一个字符
     unichr(x )              将一个整数转换为Unicode字符
     ord(x )                 将一个字符转换为它的整数值
     hex(x )                 将一个整数转换为一个十六进制字符串
     oct(x )                 将一个整数转换为一个八进制字符串
    

    参考:python类型转换

  • Python里面如何实现tuple和list的转换?

    函数tuple(seq)可以把所有可迭代的(iterable)序列转换成一个tuple, 元素不变,排序也不变。
    例如,tuple([1,2,3])返回(1,2,3), tuple(‘abc’)返回(‘a’.'b’,'c’).如果参数已经是一个tuple的话,函数不做任何拷贝而直接返回原来的对象,所以在不确定对象是不是tuple的时候来调用tuple()函数也不是很耗费的。

    函数list(seq)可以把所有的序列和可迭代的对象转换成一个list,元素不变,排序也不变。
    例如 list([1,2,3])返回(1,2,3), list(‘abc’)返回['a', 'b', 'c']。如果参数是一个list, 她会像set[:]一样做一个拷贝。

    参考:元组列表转换

  • 请写出一段Python代码实现删除一个list里面的重复元素

     aa = ["1","2","3","1"]
     list(set(aa))
    

    参考:列表去重

  • list与tuple的区别 ?dictionary与set的区别?

    思路:首先弄懂什么是list类型,什么是tuple类型,什么是dict类型和set类型

    对于list与tupel的区别,在《Python核心编程2》中讲解的很清楚,可以总体概括为,可变与不可变、他们本质相同,但是就因为可变性就延伸了很多不同点。如list类型可以进行相应更改和方法操作,但tupel就没有这个功能。

    对于dict和set,一个是Python唯一的映射类型,一个是Python集合。python中集合对象(set)是一组无序排列的可哈希的值,包含两种类型:可变集合(set)和不可变集合(frozenset),所以set不是可哈希的,frozenset是可哈希的,能当作字典的键。

    参考:python字典和集合

  • 如何理解python for else?

     for i in range(0,10):
         if i > 10:
             break;
     else:
         print "hello world";
    

    即在for 循环中,如果没有从任何一个break中退出,则会执行和for对应的else,只要从break中退出了,则else部分不执行。
    while-else/for-else语句:
    在其他语言中,除了条件语句,是不会见到else分支的,但在Python中,while和for循环中,也是可以使用else语句的。它们的工作顺序为:在循环中使用时,else语句只在循环完成后执行,也就是说,break语句也会跳过else代码块,只要循环是正常结束,而不是通过break,else语句就会执行。

    参考Python中的else语句整理

  • 介绍下你所熟悉或知道的Python库

    思路,考察对应聘者对Python了解的程度,如:

    Tkinter———— Python默认的图形界面接口。

    Python Imaging Library(PIL)————python提供强大的图形处理的能力,并提供广泛的图形文件格式支持,

    PyGame———— 用于多媒体开发和游戏软件开发的模块。

    PyQt ———— 用于python的Qt开发库。

    PyMedia ———— 用于多媒体操作的python模块。

    参考:常用的Python库

  • Python如何实现单例模式?其他23种设计模式python如何实现?

    Python有两种方式可以实现单例模式,下面两个例子使用了不同的方式实现单例模式:

    1.  class Singleton(type):
           def __init__(cls, name, bases, dict):
               super(Singleton, cls).__init__(name, bases, dict)
               cls.instance = None
      
           def __call__(cls, *args, **kw):
               if cls.instance is None:
               cls.instance = super(Singleton, cls).__call__(*args, **kw)
               return cls.instance
      
       class MyClass(object):
           __metaclass__ = Singleton
      
       print MyClass()
       print MyClass()
      
    2. 使用decorator来实现单例模式

       def singleton(cls):
           instances = {}
      
       def getinstance():
           if cls not in instances:
               instances[cls] = cls()
               return instances[cls]
           return getinstance
      
       @singleton
       class MyClass:
       …
      
  • Python里面如何拷贝一个对象?

    标准库中的copy模块提供了两个方法来实现拷贝.一个方法是copy,它返回和参数包含内容一样的对象.

     import copy
     new_list = copy.copy(existing_list)
    

    有些时候,你希望对象中的属性也被复制,可以使用deepcopy方法:

     import copy
     new_list_of_dicts = copy.deepcopy(existing_list_of_dicts)
    

    当你对一个对象赋值的时候(做为参数传递,或者做为返回值),Python和Java一样,总是传递原始对象的引用,而不是一个副本.其它一些语言当赋值的时候总是传递副本.Python从不猜测用户的需求 ,如果你想要一个副本,你必须显式的要求.
    Python的行为很简单,迅速,而且一致.然而,如果你需要一个对象拷贝而并没有显式的写出来,会出现问题的,比如:

     >>> a = [1, 2, 3]
     >>> b = a
     >>> b.append(5)
     >>> print a, b 
     [1, 2, 3, 5] [1, 2, 3, 5]
    

    在这里,变量a和b都指向同一个对象(一个列表),所以,一旦你修改了二者之一,另外一个也会受到影响.无论怎样,都会修改原来的对象。

    参考:浅复制深复制

  • 介绍一下except的用法和作用?

    Python的异常处理能力是很强大的,可向用户准确反馈出错信息。在Python中,异常也是对象,可对它进行操作。所有异常都是基类Exception的成员。所有异常都从基类Exception继承,而且都在exceptions模块中定义。Python自动将所有异常名称放在内建命名空间中,所以程序不必导入exceptions模块即可使用异常。一旦引发而且没有捕捉SystemExit异常,程序执行就会终止。如果交互式会话遇到一个未被捕捉的SystemExit异常,会话就会终止。

    方式一:try语句:

    1使用try和except语句来捕获异常

     try:
        block
     except [exception,[data…]]:
        block
    
     try:
         block
     except [exception,[data...]]:
         block
     else:
         block
    

    该种异常处理语法的规则是:

    • 执行try下的语句,如果引发异常,则执行过程会跳到第一个except语句。

    • 如果第一个except中定义的异常与引发的异常匹配,则执行该except中的语句。

    • 如果引发的异常不匹配第一个except,则会搜索第二个except,允许编写的except数量没有限制。

    • 如果所有的except都不匹配,则异常会传递到下一个调用本代码的最高层try代码中。

    • 如果没有发生异常,则执行else块代码。

    参考:python异常

  • Python中pass语句的作用是什么?

    pass语句什么也不做,一般作为占位符或者创建占位程序,pass语句不会执行任何操作, 比如:

     while False:
         pass
    

    pass通常用来创建一个最简单的类:

     class MyEmptyClass:
         pass
    

    pass在软件设计阶段也经常用来作为TODO,提醒实现相应的实现,比如:

     def initlog(*args):
         pass #please implement this
    

    参考:python pass作用

  • 介绍一下Python下range()函数的用法?

    如果需要迭代一个数字序列的话,可以使用range()函数,range()函数可以生成等差级数。

    如例:

     for i in range(5)
         print(i)
    

    这段代码将输出0, 1, 2, 3, 4五个数字

    range(10)会产生10个值, 也可以让range()从另外一个数字开始,或者定义一个不同的增量,甚至是负数增量

    range(5, 10)从5到9的五个数字range(0, 10, 3) 增量为三, 包括0,3,6,9四个数字range(-10, -100, -30) 增量为-30, 包括-10, -40, -70可以一起使用range()和len()来迭代一个索引序列例如:

     a = ['Nina', 'Jim', 'Rainman', 'Hello']
     for i in range(len(a)): 
         print(i, a[i])
    

    参考:python range

  • 如何用Python来进行查询和替换一个文本字符串?

    可以使用sub()方法来进行查询和替换,sub方法的格式为:sub(replacement, string[, count=0])

    replacement是被替换成的文本

    string是需要被替换的文本

    count是一个可选参数,指最大被替换的数量

    例子:

     import re
     p = re.compile(‘(blue|white|red)’)
     print(p.sub(‘colour’,'blue socks and red shoes’))
     print(p.sub(‘colour’,'blue socks and red shoes’, count=1))
    

    输出:

     colour socks and colour shoes
     colour socks and red shoes
    

    subn()方法执行的效果跟sub()一样,不过它会返回一个二维数组,包括替换后的新的字符串和总共替换的数量

    例如:

     import re
     p = re.compile(‘(blue|white|red)’)
     print(p.subn(‘colour’,'blue socks and red shoes’))
     print(p.subn(‘colour’,'blue socks and red shoes’, count=1))
    

    输出

     (‘colour socks and colour shoes’, 2)
     (‘colour socks and red shoes’, 1)
    
  • Python里面search()和match()的区别?

    match()函数只检测RE是不是在string的开始位置匹配, search()会扫描整个string查找匹配, 也就是说match()只有在0位置匹配成功的话才有返回,如果不是开始位置匹配成功的话,match()就返回none

    例如:
    print(re.match(‘super’, ‘superstition’).span())会返回(0, 5)
    而print(re.match(‘super’, ‘insuperable’))则返回None
    search()会扫描整个字符串并返回第一个成功的匹配

    例如:
    print(re.search(‘super’, ‘superstition’).span())返回(0, 5)
    print(re.search(‘super’, ‘insuperable’).span())返回(2, 7)

  • 用Python匹配HTML tag的时候,<.*><.*?>有什么区别?

    当重复匹配一个正则表达式时候, 例如<.*>, 当程序执行匹配的时候,会返回最大的匹配值

    例如:

     import re
     s = ‘<html><head><title>Title</title>’
     print(re.match(‘<.*>’, s).group())
    

    会返回一个匹配<html><head><title>Title</title>而不是<html>

    而:

     import re
     s = ‘<html><head><title>Title</title>’
     print(re.match(‘<.*?>’, s).group())
    

    则会返回<html>

    <.*>这种匹配称作贪心匹配
    <.*?>称作非贪心匹配

  • Python里面如何生成随机数?

    random.random()用于生成一个指定范围内的随机符点数,两个参数其中一个是上限,一个是下限。如果a > b,则生成随机数

     n: a <= n <= b。如果 a <b, 则 b <= n <= a。
    
     print random.uniform(10, 20)  
     print random.uniform(20, 10)  
     #---- 
     #18.7356606526  
     #12.5798298022  
    

    random.randint()用于生成一个指定范围内的整数。其中参数a是下限,参数b是上限,

     print random.randint(12, 20) #生成的随机数n: 12 <= n <= 20 
     print random.randint(20, 20) #结果永远是20 
     #print random.randint(20, 10) #该语句是错误的。 
    

    下限必须小于上限。

    random.randrange()从指定范围内,生成按指定基数递增的集合。

    随机整数:

     >>> import random
     >>> random.randint(0,99)
     21
    

    随机选取0到100间的偶数:

     >>> import random
     >>> random.randrange(0, 101, 2)
     42
    

    随机浮点数:

     >>> import random
     >>> random.random() 
     0.85415370477785668
     >>> random.uniform(1, 10)
     5.4221167969800881
    

    随机字符:

     >>> import random
     >>> random.choice('abcdefg&#%^*f')
     'd'
    

    多个字符中选取特定数量的字符:

     >>> import random
     random.sample('abcdefghij',3) 
     ['a', 'd', 'b']
    

    多个字符中选取特定数量的字符组成新字符串:

     >>> import random
     >>> import string
     >>> string.join(random.sample(['a','b','c','d','e','f','g','h','i','j'], 3)).replace(" ","")
     'fih'
    

    随机选取字符串:

     >>> import random
     >>> random.choice ( ['apple', 'pear', 'peach', 'orange', 'lemon'] )
     'lemon'
    

    洗牌:

     >>> import random
     >>> items = [1, 2, 3, 4, 5, 6]
     >>> random.shuffle(items)
     >>> items
     [3, 2, 5, 6, 4, 1]
    
  • 如何用Python来发送邮件?

    可以使用smtplib标准库。

    以下代码可以在支持SMTP监听器的服务器上执行。

     import sys, smtplib
    
     fromaddr = raw_input("From: ")
     toaddrs = raw_input("To: ").split(",")
     print "Enter message, end with ^D:"
     msg = ""
     while 1:
         line = sys.stdin.readline()
         if not line:
             break
     msg = msg + line
    
     # 发送邮件部分
     server = smtplib.SMTP(‘localhost’)
     server.sendmail(fromaddr, toaddrs, msg)
     server.quit()
    
  • 有两个序列a,b,大小都为n,序列元素的值任意整形数,无序;
    要求:通过交换a,b中的元素,使[序列a元素的和]与[序列b元素的和]之间的差最小。

    1. 将两序列合并为一个序列,并排序,为序列Source
    2. 拿出最大元素Big,次大的元素Small
    3. 在余下的序列S[:-2]进行平分,得到序列max,min
    4. 将Small加到max序列,将Big加大min序列,重新计算新序列和,和大的为max,小的为min。
  • Python如何定义一个函数?

    函数的定义形式如下:

     def (arg1, arg2,… argN): 
    

    函数的名字也必须以字母开头,可以包括下划线“ ”,但不能把Python的关键字定义成函数的名字。函数内的语句数量是任意的,每个语句至少有一个空格的缩进,以表示此语句属于这个函数的。缩进结束的地方,函数自然结束。
    下面定义了一个两个数相加的函数:

     >>> def add(p1, p2): 
         print p1, “+”, p2, “=”, p1+p2 
     >>> add(1, 2) 
     1 + 2 = 3 
    

    函数的目的是把一些复杂的操作隐藏,来简化程序的结构,使其容易阅读。函数在调用前,必须先定义。也可以在一个函数内部定义函数,内部函数只有在外部函数调用时才能够被执行。程序调用函数时,转到函数内部执行函数内部的语句,函数执行完毕后,返回到它离开程序的地方,执行程序的下一条语句。

  • 有没有一个工具可以帮助查找python的bug和进行静态的代码分析?

    有,PyChecker是一个python代码的静态分析工具,它可以帮助查找python代码的bug, 会对代码的复杂度和格式提出警告

    Pylint是另外一个工具可以进行coding standard检查。

  • 如何在一个function里面设置一个全局的变量?

    解决方法是在function的开始插入一个global声明:

     def f():
         global x
    
  • 听过 the zen of python 吗?尽可能地默写它,中英文皆可,大意也可。如果没有听过,谈谈你对 pythonic 的看法,或者你认为什么样的 python 代码才是好代码。

    在python中import this就会展示出The Zen of Python如下:

    The Zen of Python, by Tim Peters

    Beautiful is better than ugly.
    Explicit is better than implicit.
    Simple is better than complex.
    Complex is better than complicated.
    Flat is better than nested.
    Sparse is better than dense.
    Readability counts.
    Special cases aren't special enough to break the rules.
    Although practicality beats purity.
    Errors should never pass silently.
    Unless explicitly silenced.
    In the face of ambiguity, refuse the temptation to guess.
    There should be one-- and preferably only one --obvious way to do it.
    Although that way may not be obvious at first unless you're Dutch.
    Now is better than never.
    Although never is often better than right now.
    If the implementation is hard to explain, it's a bad idea.
    If the implementation is easy to explain, it may be a good idea.
    Namespaces are one honking great idea -- let's do more of those!

    这被称为python之禅,以下是翻译

    Python之禅

    优美胜于丑陋(Python 以编写优美的代码为目标)
    明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
    简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
    复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
    扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
    间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
    可读性很重要(优美的代码是可读的)
    即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)
    不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except:pass 风格的代码)
    当存在多种可能,不要尝试去猜测
    而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)
    虽然这并不容易,因为你不是 Python 之父(这里的 Dutch 是指 Guido )
    做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量)
    如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)
    命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)

    参考:pythonic

  • 现在有一个 dict 对象 adict,里面包含了一百万个元素,查找其中的某个元素的平均需要多少次比较?一千万个元素呢?

    python dict和set都是使用hash表来实现(类似c++11标准库中unordered_map),查找元素的时间复杂度是O(1)

     a = range(1000)
     s = set(a)
     d = dict((i,1) for i in a)
     %timeit -n 10000 100 in d
     %timeit -n 10000 100 in s
     10000 loops, best of 3: 43.5 ns per loop
     10000 loops, best of 3: 49.6 ns per loop
    

    dict的效率略高(占用的空间也多一些)。

  • 现在有一个 list 对象 alist,里面的所有元素都是字符串,编写一个函数对它实现一个大小写无关的排序。

     a = ['a', 'A', 'b', 'C', 'd']
     b = [i.lower() for i in a]
     b.sort()
    
  • python 里关于“堆”这种数据结构的模块是哪个?“堆”有什么优点和缺点?举一个游戏开发中可能会用到堆的问题(不限是于 python 的堆,可以是其它语言的相关实现)。

    heapq模块使用一个用堆实现的优先级队列。堆是一种简单的有序列表,并且置入了堆的相关规则。

    堆是一种树形的数据结构,树上的子节点与父节点之间存在顺序关系。二叉堆(binary heap)能够用一个经过组织的列表或数组结构来标识,在这种结构中,元素N的子节点的序号为2N+1和2N+2(下标始于0)。简单来说,这个模块中的所有函数都假设序列是有序的,所以序列中的第一个元素(seq[

     import heapq
    
     heap = []
    
     for value in [20, 10, 30, 50, 40]:
         heapq.heappush(heap, value)
    
     while heap:
         print heapq.heappop(heap)
    

    heapq模块有两个函数nlargest()和nsmallest(),顾名思义,让我们来看看它们的用法。

     import heapq
    
     nums = [1, 8, 2, 23, 7, -4, 18, 23, 42, 37, 2]
     print(heapq.nlargest(3, nums)) # Prints [42, 37, 23]
     print(heapq.nsmallest(3, nums)) # Prints [-4, 1, 2]
    

    两个函数也能够通过一个键参数使用更为复杂的数据结构,例如:

     import heapq
    
     portfolio = [
     {'name': 'IBM', 'shares': 100, 'price': 91.1},
     {'name': 'AAPL', 'shares': 50, 'price': 543.22},
     {'name': 'FB', 'shares': 200, 'price': 21.09},
     {'name': 'HPQ', 'shares': 35, 'price': 31.75},
     {'name': 'YHOO', 'shares': 45, 'price': 16.35},
     {'name': 'ACME', 'shares': 75, 'price': 115.65}
     ]
     cheap = heapq.nsmallest(3, portfolio, key=lambda s: s['price'])
     expensive = heapq.nlargest(3, portfolio, key=lambda s: s['price'])
    
     print cheap
    
     # [{'price': 16.35, 'name': 'YHOO', 'shares': 45},
     # {'price': 21.09, 'name': 'FB', 'shares': 200}, {'price': 31.75, 'name': 'HPQ', 'shares': 35}]
    
     print expensive
    
     # [{'price': 543.22, 'name': 'AAPL', 'shares': 50}, {'price': 115.65, 'name': 'ACME', 
     # 'shares': 75}, {'price': 91.1, 'name': 'IBM', 'shares': 100}]
    

    来看看如何实现一个根据给定优先级进行排序,并且每次pop操作都返回优先级最高的元素的队列例子。

     import heapq
    
     class Item:
         def __init__(self, name):
             self.name = name
    
         def __repr__(self):
             return 'Item({!r})'.format(self.name)
    
     class PriorityQueue:
         def __init__(self):
             self._queue = []
             self._index = 0
    
         def push(self, item, priority):
             heapq.heappush(self._queue, (-priority, self._index, item))
             self._index += 1
    
         def pop(self):
             return heapq.heappop(self._queue)[-1]
    
     q = PriorityQueue()
     q.push(Item('foo'), 1)
     q.push(Item('bar'), 5)
     q.push(Item('spam'), 4)
     q.push(Item('grok'), 1)
    
     print q.pop() # Item('bar')
     print q.pop() # Item('spam')
     print q.pop() # Item('foo')
     print q.pop() # Item('grok')
    

    数据结构优缺点

     类型  优点  缺点
     数组  插入块,如果知道下标,可以非常快的存储 查找慢,删除慢,大小固定
     有序数组    比无序数组查找快    删除和插入慢,大小固定
     栈   提供后进先出方式的存取 效率低
     队列  提供先进先出的方式存取 效率低
     链表  插入,删除快  查找慢
     二叉树 查找,插入,删除都快  如果非平衡就很慢,删除的算法复杂
     红黑树 查找,插入,删除都快  算法复杂
     哈希表 如果关键字已知则存取极快,插入块    删除慢,如果不知道关键字则存取很慢,对存储空间使用不充分
     堆   插入,删除快,对最大数据项的存取很快  对其他存储项很慢
    
  • set 是在哪个版本成为 build-in types 的?举一个你在以往项目中用到这种数据结构的问题(不限是于 python 的 set ,可以是其它语言的相关实现),并说明为什么当时选择了 set 这种数据结构。

    set跟list一样可以用{}来定义,但是pyton -v 版本 >2.7
    set的数据格式:

     [html] view plaincopy
     s1={"abc","def"}      #{} 自己定义  
     >>> print s1  
     set(['abc', 'def'])    
     >>> s2=set("abcdef")  #set 函数用string进行初始化  
     >>> print s2  
     set(['a', 'c', 'b', 'e', 'd', 'f'])  
     >>> s3=set(["abc",123,"def"])  #set 函数用list进行初始化  
     >>> print s3  
     set([123, 'abc', 'def'])  
    
  • 有一个排好序地 list 对象 alist,查找其中是否有某元素 a(尽可能地使用标准库函数)。

    index() 函数用于从列表中找出某个值第一个匹配项的索引位置。

     list.index(obj) # obj -- 查找的对象。
    

    该方法返回查找对象的索引位置,如果没有找到对象则抛出异常。

    count() 方法用于统计某个元素在列表中出现的次数。

     list.count(obj) # obj -- 列表中统计的对象。
    

    返回元素在列表中出现的次数。

  • 说说 dict 的 items() 方法与 iteritems() 方法的不同。

     #字典items()的使用
     dict = {"a" : "apple", "b" : "banana", "c" : "grape", "d" : "orange"}
     #每个元素是一个key和value组成的元组,以列表的方式输出
     print dict.items()
     #调用items()实现字典的遍历
     dict = {"a" : "apple", "b" : "banana", "g" : "grape", "o" : "orange"}
     for (k, v) in dict.items():
         print "dict[%s] =" % k, v
     #调用iteritems()实现字典的遍历
     dict = {"a" : "apple", "b" : "banana", "c" : "grape", "d" : "orange"}
     print dict.iteritems()
     for k, v in dict.iteritems():
         print "dict[%s] =" % k, v
     for (k, v) in zip(dict.iterkeys(), dict.itervalues()):
         print "dict[%s] =" % k, v
    

    python3.0以上,不存在dict.iteritems()这个函数。

  • 写一段程序逐行读入一个文本文件,并在屏幕上打印出来。

     file_object = open('thefile.txt')  
     try:  
          for line in file_object:
              print line.rstrip()
     finally:  
          file_object.close()
    
  • 默写尽可能多的 str 对象的方法。

    在python 中任何事物都是对象,就是万物皆对象,同时字符串也是一个对象,当你调用一些基本的操作方法时,会自动调用类定义的内建方法,在python中方法主要三种

    1. 一般的工友方法(默认)命名:和一般的函数命名规则相同

    2. 私有方法命名:一般以__开始

    3. 内建的方法,有时你也需要重写或重载 比如init构造方法
      下面具体来看一下str的类方法

      class str(basestring)

      | str(object) ->string
      |
      | Return a nice stringrepresentation of the object.
      | If the argument is a string,the return value is the same object.
      |
      | Method resolutionorder:
      | str
      | basestring
      | object
      |
      | Methods defined here:
      |
      | add(...)
      | x.add(y) <==> x+y
      |
      | contains(...)
      | x.contains(y) <==> y in x
      |
      | eq(...)
      | x.eq(y) <==> x==y
      |
      | format(...)
      | S.format(format_spec) -> unicode
      |
      | ge(...)
      | x.ge(y) <==>x>=y
      |
      | getattribute(...)
      | x.getattribute('name') <==>x.name
      |
      | getitem(...)
      | x.getitem(y) <==> x[y]
      |
      | getnewargs(...)
      |
      | getslice(...)
      | x.getslice(i, j) <==>x[i:j]
      |
      | Use of negative indices is not supported.
      |
      | gt(...)
      | x.gt(y) <==>x>y
      |
      | hash(...)
      | x.hash() <==> hash(x)
      |
      | le(...)
      | x.le(y) <==>x<=y
      |
      | len(...)
      | x.len() <==> len(x)
      |
      | lt(...)
      | x.lt(y) <==>x<y
      |
      | mod(...)
      | x.mod(y) <==> x%y
      |
      | mul(...)
      | x.mul(n) <==> xn
      |
      | ne(...)
      | x.ne(y) <==> x!=y
      |
      | repr(...)
      | x.repr() <==> repr(x)
      |
      | rmod(...)
      | x.rmod(y) <==> y%x
      |
      | rmul(...)
      | x.rmul(n) <==> n
      x
      |
      | sizeof(...)
      | S.sizeof() -> size of S in memory, inbytes
      |
      | str(...)
      | x.str() <==> str(x)
      |
      | capitalize(...)
      | S.capitalize() -> string
      |
      | Return a copy of the string S with only its first character
      | capitalized.
      |
      | center(...)
      | S.center(width[, fillchar]) -> string
      |
      | Return S centered in a string of length width. Padding is
      | done using the specified fill character (default is a space)
      |
      | count(...)
      | S.count(sub[, start[, end]]) -> int
      |
      | Return the number of non-overlapping occurrences of substring subin
      | string S[start:end]. Optional arguments start andend are interpreted
      | as in slice notation.
      |
      | decode(...)
      | S.decode([encoding[,errors]]) -> object
      |
      | Decodes S using the codec registered for encoding. encodingdefaults
      | to the default encoding. errors may be given to set a differenterror
      | handling scheme. Default is 'strict' meaning that encoding errorsraise
      | a UnicodeDecodeError. Other possible values are 'ignore' and'replace'
      | as well as any other name registered with codecs.register_errorthat is
      | able to handle UnicodeDecodeErrors.
      |
      | encode(...)
      | S.encode([encoding[,errors]]) -> object
      |
      | Encodes S using the codec registered for encoding. encodingdefaults
      | to the default encoding. errors may be given to set a differenterror
      | handling scheme. Default is 'strict' meaning that encoding errorsraise
      | a UnicodeEncodeError. Other possible values are 'ignore', 'replace'and
      | 'xmlcharrefreplace' as well as any other name registered with
      | codecs.register_error that is able to handleUnicodeEncodeErrors.
      |
      | endswith(...)
      | S.endswith(suffix[, start[, end]]) -> bool
      |
      | Return True if S ends with the specified suffix, Falseotherwise.
      | With optional start, test S beginning at that position.
      | With optional end, stop comparing S at that position.
      | suffix can also be a tuple of strings to try.
      |
      | expandtabs(...)
      | S.expandtabs([tabsize]) -> string
      |
      | Return a copy of S where all tab characters are expanded usingspaces.
      | If tabsize is not given, a tab size of 8 characters isassumed.
      |
      | find(...)
      | S.find(sub [,start [,end]]) -> int
      |
      | Return the lowest index in S where substring sub is found,
      | such that sub is contained within s[start:end]. Optional
      | arguments start and end are interpreted as in slice notation.
      |
      | Return -1 on failure.
      |
      | format(...)
      | S.format(*args, **kwargs) -> unicode
      |
      | index(...)
      | S.index(sub [,start [,end]]) -> int
      |
      | Like S.find() but raise ValueError when the substring is notfound.
      |
      | isalnum(...)
      | S.isalnum() -> bool
      |
      | Return True if all characters in S are alphanumeric
      | and there is at least one character in S, False otherwise.
      |
      | isalpha(...)
      | S.isalpha() -> bool
      |
      | Return True if all characters in S are alphabetic
      | and there is at least one character in S, False otherwise.
      |
      | isdigit(...)
      | S.isdigit() -> bool
      |
      | Return True if all characters in S are digits
      | and there is at least one character in S, False otherwise.
      |
      | islower(...)
      | S.islower() -> bool
      |
      | Return True if all cased characters in S are lowercase and thereis
      | at least one cased character in S, False otherwise.
      |
      | isspace(...)
      | S.isspace() -> bool
      |
      | Return True if all characters in S are whitespace
      | and there is at least one character in S, False otherwise.
      |
      | istitle(...)
      | S.istitle() -> bool
      |
      | Return True if S is a titlecased string and there is at leastone
      | character in S, i.e. uppercase characters may only followuncased
      | characters and lowercase characters only cased ones. ReturnFalse
      | otherwise.
      |
      | isupper(...)
      | S.isupper() -> bool
      |
      | Return True if all cased characters in S are uppercase and thereis
      | at least one cased character in S, False otherwise.
      |
      | join(...)
      | S.join(sequence) -> string
      |
      | Return a string which is the concatenation of the strings inthe
      | sequence. The separator between elements isS.
      |
      | ljust(...)
      | S.ljust(width[, fillchar]) -> string
      |
      | Return S left-justified in a string of length width. Paddingis
      | done using the specified fill character (default is a space).
      |
      | lower(...)
      | S.lower() -> string
      |
      | Return a copy of the string S converted to lowercase.
      |
      | lstrip(...)
      | S.lstrip([chars]) -> string or unicode
      |
      | Return a copy of the string S with leading whitespaceremoved.
      | If chars is given and not None, remove characters in charsinstead.
      | If chars is unicode, S will be converted to unicode beforestripping
      |
      | partition(...)
      | S.partition(sep) -> (head, sep, tail)
      |
      | Search for the separator sep in S, and return the part beforeit,
      | the separator itself, and the part after it. Ifthe separator is not
      | found, return S and two empty strings.
      |
      | replace(...)
      | S.replace (old, new[, count]) -> string
      |
      | Return a copy of string S with all occurrences of substring
      | old replaced by new. If the optional argumentcount is
      | given, only the first count occurrences are replaced.
      |
      | rfind(...)
      | S.rfind(sub [,start [,end]]) -> int
      |
      | Return the highest index in S where substring sub is found,
      | such that sub is contained within s[start:end]. Optional
      | arguments start and end are interpreted as in slice notation.
      |
      | Return -1 on failure.
      |
      | rindex(...)
      | S.rindex(sub [,start [,end]]) -> int
      |
      | Like S.rfind() but raise ValueError when the substring is notfound.
      |
      | rjust(...)
      | S.rjust(width[, fillchar]) -> string
      |
      | Return S right-justified in a string of length width. Paddingis
      | done using the specified fill character (default is a space)
      |
      | rpartition(...)
      | S.rpartition(sep) -> (tail, sep, head)
      |
      | Search for the separator sep in S, starting at the end of S, andreturn
      | the part before it, the separator itself, and the part afterit. If the
      | separator is not found, return two empty strings and S.
      |
      | rsplit(...)
      | S.rsplit([sep [,maxsplit]]) -> list of strings
      |
      | Return a list of the words in the string S, using sep as the
      | delimiter string, starting at the end of the string andworking
      | to the front. If maxsplit is given, at mostmaxsplit splits are
      | done. If sep is not specified or is None, any whitespacestring
      | is a separator.
      |
      | rstrip(...)
      | S.rstrip([chars]) -> string or unicode
      |
      | Return a copy of the string S with trailing whitespaceremoved.
      | If chars is given and not None, remove characters in charsinstead.
      | If chars is unicode, S will be converted to unicode beforestripping
      |
      | split(...)
      | S.split([sep [,maxsplit]]) -> list of strings
      |
      | Return a list of the words in the string S, using sep as the
      | delimiter string. If maxsplit is given, at mostmaxsplit
      | splits are done. If sep is not specified or is None, any
      | whitespace string is a separator and empty strings areremoved
      | from the result.
      |
      | splitlines(...)
      | S.splitlines([keepends]) -> list of strings
      |
      | Return a list of the lines in S, breaking at line boundaries.
      | Line breaks are not included in the resulting list unlesskeepends
      | is given and true.
      |
      | startswith(...)
      | S.startswith(prefix[, start[, end]]) -> bool
      |
      | Return True if S starts with the specified prefix, Falseotherwise.
      | With optional start, test S beginning at that position.
      | With optional end, stop comparing S at that position.
      | prefix can also be a tuple of strings to try.
      |
      | strip(...)
      | S.strip([chars]) -> string or unicode
      |
      | Return a copy of the string S with leading and trailing
      | whitespace removed.
      | If chars is given and not None, remove characters in charsinstead.
      | If chars is unicode, S will be converted to unicode beforestripping
      |
      | swapcase(...)
      | S.swapcase() -> string
      |
      | Return a copy of the string S with uppercase characters
      | converted to lowercase and vice versa.
      |
      | title(...)
      | S.title() -> string
      |
      | Return a titlecased version of S, i.e. words start withuppercase
      | characters, all remaining cased characters have lowercase.
      |
      | translate(...)
      | S.translate(table [,deletechars]) -> string
      |
      | Return a copy of the string S, where all characters occurring
      | in the optional argument deletechars are removed, and the
      | remaining characters have been mapped through the given
      | translation table, which must be a string of length 256.
      |
      | upper(...)
      | S.upper() -> string
      |
      | Return a copy of the string S converted to uppercase.
      |
      | zfill(...)
      | S.zfill(width) -> string
      |
      | Pad a numeric string S with zeros on the left, to fill afield
      | of the specified width. The string S is nevertruncated.

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

     import random
     b = ['a', 'a', 'b', 'c', 'd']
     random.shuffle(b)
    
  • 有二维的 list 对象(即它的每一个元素都是一个 list 对象)alist,假定其中的所有元素都具有相同的长度(把 alist 想象成一个表格),写一段程序根据元素的第二个元素排序(即对表格的第二列字段排序)。

     a = [['3', 'b'], ['2', 'd'], ['1', 'c'], ['4', 'a']]
     sorted(a, key=lambda a:a[1])
    

    参考:python中sorted内建函数的用法

  • 实现一个 stack。

     class Stack :
    
         # Creates an empty stack.
         def __init__( self ):
             self._theItems = list()
    
         # Returns True if the stack is empty or False otherwise.
         def isEmpty( self ):
             return len( self ) == 0
    
         # Returns the number of items in the stack.
         def __len__ ( self ):
             return len( self._theItems )
    
         # Returns the top item on the stack without removing it.
         def peek( self ):
             assert not self.isEmpty(), "Cannot peek at an empty stack"
             return self._theItems[-1]
    
         # Removes and returns the top item on the stack.
         def pop( self ):
             assert not self.isEmpty(), "Cannot pop from an empty stack"
             return self._theItems.pop()
    
         # Push an item onto the top of the stack.
         def push( self, item ):
             self._theItems.append( item )
    
  • 编写一个简单的 ini 文件解释器。

    ConfigParser 是用来读取配置文件的包。配置文件的格式如下:中括号“[ ]”内包含的为section。section 下面为类似于key-value 的配置内容。

     [db]
     db_host = 127.0.0.1
     db_port = 22
     db_user = root
     db_pass = rootroot
     
     [concurrent]
     thread = 10
     processor = 20
    

    中括号“[ ]”内包含的为section。紧接着section 为类似于key-value 的options 的配置内容。

    ConfigParser 初始工作
    使用ConfigParser 首选需要初始化实例,并读取配置文件:

     cf = ConfigParser.ConfigParser()
     cf.read("配置文件名")
    

    ConfigParser 常用方法

    1. 获取所有sections。也就是将配置文件中所有“[ ]”读取到列表中:

       s = cf.sections()
       print 'section:', s
      

      将输出(以下将均以简介中配置文件为例):

       section: ['db', 'concurrent']
      
    2. 获取指定section 的options。即将配置文件某个section 内key 读取到列表中:

       o = cf.options("db")
       print 'options:', o
      

      将输出:

       options: ['db_host', 'db_port', 'db_user', 'db_pass']
      
    3. 获取指定section 的配置信息。

       v = cf.items("db")
       print 'db:', v
      

      将输出:

       db: [('db_host', '127.0.0.1'), ('db_port', '22'), ('db_user', 'root'), ('db_pass', 'rootroot')]
      
    4. 按照类型读取指定section 的option 信息。
      同样的还有getfloat、getboolean。

       #可以按照类型读取出来
       db_host = cf.get("db", "db_host")
       db_port = cf.getint("db", "db_port")
       db_user = cf.get("db", "db_user")
       db_pass = cf.get("db", "db_pass")
       
       # 返回的是整型的 
       threads = cf.getint("concurrent", "thread")
       processors = cf.getint("concurrent", "processor")
      
       print "db_host:", db_host
       print "db_port:", db_port
       print "db_user:", db_user
       print "db_pass:", db_pass
       print "thread:", threads
       print "processor:", processors
      

      将输出:

       db_host: 127.0.0.1
       db_port: 22
       db_user: root
       db_pass: rootroot
       thread: 10
       processor: 20
      
    5. 设置某个option 的值。(记得最后要写回)

       cf.set("db", "db_pass", "zhaowei")
       cf.write(open("test.conf", "w"))
      

    6.添加一个section。(同样要写回)

         cf.add_section('liuqing')
         cf.set('liuqing', 'int', '15')
         cf.set('liuqing', 'bool', 'true')
         cf.set('liuqing', 'float', '3.1415')
         cf.set('liuqing', 'baz', 'fun')
         cf.set('liuqing', 'bar', 'Python')
         cf.set('liuqing', 'foo', '%(bar)s is %(baz)s!')
         cf.write(open("test.conf", "w"))
    
    1. 移除section 或者option 。(只要进行了修改就要写回的哦)

       cf.remove_option('liuqing','int')
       cf.remove_section('liuqing')
       cf.write(open("test.conf", "w"))
      
  • 现有 N 个纯文本格式的英文文件,实现一种检索方案,即做一个小搜索引擎。

  • python自动连接ssh的代码

    python自动连接ssh的代码:

     #!/usr/bin/python
     #-*- coding:utf-8 -*-
    
     import sys, time, os
    
     try:
         import pexpect
     except ImportError:
         print """
             You must install pexpect module
         """
         sys.exit(1)
    
     addr_map = {
         'v3' :('root@192.168.1.162', 'sina@2009'),
         'dev':('test016@192.168.1.136', 'test016'),
         }
    
     try:
         key = sys.argv[1]
         host = addr_map[key]
     except:
         print """
             argv error, use it link
             jssh v3, v3 must defined in addr_map
         """
     sys.exit(1)
    
     server = pexpect.spawn('/usr/bin/ssh %s' % host[0])
     server.expect('.*ssword:')
     server.sendline(host[1])
     server.interact()
    
  • 用Python写一个小小的爬虫程序

    Python有一个urllib的库,可以很方便的从给定的url抓取网页,以下这段程序实现了抓取一个url并存到指定文件的功能:

     def downURL(url, filename):
         try:
             fp = urllib2.rulopen(url)
         except:
             print 'download exception'
             return 0
         while 1:
             s = fp.read()
             if not s:
                 break
             op.write(s)
         
         fp.close()
         op.close()
    

    爬虫工作的基本原理就是,给定一个初始的url,下载这个url的网页,然后找出网页上所有满足下载要求的链接,然后把这些链接对应的url下载下来,然后再找下载下来的这些网页的url,我们可以用广度优先搜索实现这个算法,不过,首先得有一个函数找出网页上所有的满足要求的url,下面这个例子用正则表达式找出url。

     def getURL(url):
         try:
             fp = urllib2.urlopen(url)
         except:
             print 'get url exception'
             return []
    
         pattern = re.compile(""http://sports.sina.com.cn/[^\>]+.shtml")
         while 1:
             s = fp.read()
             if not s:
                 break
             urls = pattern.findall(s)
         fp.close()
         return urls
    

    最后就是广度优先搜索了,这个实现起来也很简单:

     def spider(startURL, times):
         urls = []
         urls.append(startURL)
         i = 0
         while 1:
             if i>times:
                 break;
             if len(urls)>0:
                 url = urls.pop(0)
                 print url, len(urls)
                 downURL(url,str(i)+'.htm')
                 i = i + 1
                 if len(urls)<times:
                     urllist = getURL(url)
                     for url in urllist:
                         if urls.count(url) == 0:
                             urls.append(url)
             else:
                 break
         return 1
    
  • 输出一个[1,2,3,...,100]的序列,不用循环、控制语句、函数等。

     l = range(1,101)
     print l
    

部分from 豆瓣

做一件事需要几个步骤

大约是13年的10月开始,我在公司里从事着黑盒测试的工作。当时我的任务是要对服务端的接口进行测试。为了能更好(偷懒)的完成好工作,我开发了一个简单的自动化接口测试程序personal,她可以根据我提交的测试数据、接口文档,自动发送报文进行测试并收集测试结果,然后生成报表发邮件通知我。这本来也没什么值得可说的,本来没打算有大用的小程序,随着被头头的喜爱而不断对其的功能进行扩充。于是,本来没有太经过设计规划的代码喜闻乐见的越来越冗余,连我自己都不忍落目。

于是,在一次次下决心之后,我打算对之前的小程序进行重新设计、重写。这也算是对以前工作的一个重新思考、总结,也期待经过这样一次回顾后,我能对python、自动化测试、框架有更深的理解。当然,这些都是垃圾话,问题是,怎样的一个设计才能更灵活,并且在不同项目之间,也能很快的移植、定制。

我果然又标题党了么 (●´ϖ`●) ?这些貌似跟标题没多大的关系,在说这个之前,我想先谈谈qml中关于Rectangle的若干思考。


自从大约是去年6月左右转了Qt开发之后,就掉进了qml的世界。刚接触时,第一眼觉得qml真的跟css长的很像很像,再加上曾经有用PyQt库的经验,学起来还是不是很难理解的。这半年以来,对qml的理解越来越深,觉得qml真的是很有趣的一个东西。在qml里第一个接触到的组件就是Rectangle,矩形,其实就是一个方块嘛,但是在很多可视的控件设计中都少不了它。

Rectangle

唔,好吧,其实我只是手欠,所以画了个Rectangle的示意图。

刚上手Qt的时候,是在老手的带领下,拼装拼装小组件,拼装拼装页面啥的。用“拼装”这个词来描述可是一点都不虚。我那会所做的就是简单的把组件和组件组合在一起,然后就行啦。于是,对那时的我来说,Rectangle就是一个神奇玩意了,为什么这么说呢。比如,你想自己做一个按钮,一被点击就触发一个事件。(我知道还有一个叫做button的控件,但是让我们忽略它。)那就可以画一个Rectangle上面在盖一个MouseArea,就可以了。ヾ( ´ ▽ ‘ )ノ

Button代码示例

Button

//MyButton.qml
import QtQuick 2.4

Rectangle {
    id:button
    implicitWidth: 100
    implicitHeight: 62
    color:"red"

    signal clicked //点击事件

    MouseArea {
        anchors.fill: parent

        onClicked: {
            button.forceActiveFocus()
            button.clicked()
        }
    }
}

再比如说,你打算弄个霸气大板砖形的输入框,最简单的方法就是弄个Rectangle上面放个TextInput,当然也可以通过定制style来实现,不过总觉得麻烦,不如这样简单粗暴。(=。= )

TextinputBar代码示例

TextinputBar

//MyTextinputBar.qml
import QtQuick 2.4

Rectangle {
    id:textinputbar
    implicitWidth: 100
    implicitHeight: 62
    color:"yellow"
    clip:true

    TextInput {
        anchors.fill: parent
    }
}

在每个qml文件里,组件们被串成了一棵树。在久了之后,有时候我就会想知道,为什么是一种树形结构,或者说这种结构有什么样的好处。并且,在每个qml页面里,起码要有一个根组件,也就是说,你不能创建一个全空白的qml,而这又是为何?

当我试图换个角度来重新看待Rectangle这件事时,我觉得我又对qml有了更深的理解。每个qml文件的根组件就好比是一个类,在其中的其他组件就是那些组件类的一个实例,各种继承、各种封装。Rectangle、Text等是继承Item的,ListView各种wiew是继承Flickable的,Flickable也是继承Item。Item是继承QtObject的……于是,一个qml组件的金字塔一层层的展现出来。


那么,回到我那个倒霉悲催的personal程序上,在这一次的重写计划中,我一直在思考,从qml中学习到的东西,有没有我可以加以借鉴学习的。

personal流程

当初的整个personal程序的大体流程就是如此。我的整个程序的输入是用户的测试用例(集),以及由于是要对接口和后台服务进行测试,我需要读取与服务器通讯的接口配置文档。得到之后,根据用例集以及用例顺序,有序的组装报文,以便于进行测试。当得到报文后,发送报文到被测程序开始测试。被测后台服务会根据接口给我返回报文,我收获返回报文后,对报文进行解析,判断报文格式是否正确,报文内的数据是否与用户之前预留的一致,并将结果记录下来。整个测试结束后,开始统计测试结果、计算通过率、绘制图表,然后生成测试报告,打包将其发送到测试人员邮箱。

当时的我,并没有对程序进行仔细的结构设计,于是我就按照流程,就那么面向过程的实现了整个程序还一度沾沾自喜。于是问题来了,当我有序(悠闲)的用这个程序进行着测试工作时,头头觉得我写的这个很方便,并且每天下班后,服务器自己还可以默默的开始几轮测试简直是太攒了之后。小小的personal程序的需求突然变的开始爆炸,更多的接口、不同的报文格式、验证返回报文内容加上逻辑运算、嵌套多层的测试数据等等,为了适应这些需求,程序变得越来越难改、越改越不好用,我开始头疼了…… (๑´灬`๑)

其实,如果在站的高点来看整个pensonal程序,她所做的事不过就是准备测试开始测试测试结果分析,这三大部分。在准备测试里,读取各种数据、组装报文、得到真实用例集;开始测试里,发送报文并接收返回报文、对返回报文进行验证、收集结果;测试结果分析里,统计结果、根据结果生成各种报表等、发送结果。整个流程和每层内部都看着即熟悉又陌生,有那么一点点相似。如果在站的高一点呢,其实我把每个部分都分成了3个部分,即pre、do、after。

do one thing

于是,我可以再抽象一下,定义一个类。这个类它做一件事,并将此事可以分为3个步骤,分别为准备做开始做后续处理。在此基础上,我可以定义上面三大部分里的每一个小类、以及准备测试、开始测试、测试结果分析等等。这样,就像qml的金字塔一样,一层层的搭起来我的测试程序。

(ง •̀_•́)ง 当然这只是我现在最初的构想,细节部分完全没有涉及、譬如插件系统、并发等等,但是总归要一步一步不是,这一次,我定要想好在动笔。哦,回到那个最初的问题上,做一件事需要几个步骤?对于当下的我来说,应该是3步吧~~ (//▽//)