从Python转到Golang

所有的抉择都有因为产品的需要

Go诞生于Google, 是由以下三位计算机领域的大师写的

image

    由于出身名门,Go在诞生之初就吸引了大批开发者的关注。诞生十年以来,已经有很多基于Go的应用,一直有传言Go在将来是要取代现在Java的位置。对于一门只有十年历史的新语言,Go的发展势头是相当迅猛的。国外的docker就是用Go写的,国内的七牛团队广泛使用Go。
    而今如火如荼的区块链技术更是将Go推上了热潮。IOT设备连接协议也是Go语言开发技术居多。由此可见未来是属于Go和Python的天下。

go环境搭建

官网下载go的安装包
如果是mac,可直接用brew安装:

$ brew intall go

查看go版本

$ go version
go version go1.10.3 darwin/amd64

go环境变量配置

GOROOT golang安装路径
GOPATH go工作环境中常常用到的一个很重要的环境变量。具体用途:go命令常常需要用到的,
如go run,go install, go get等。
GOBIN go install编译存放路径。不允许设置多个路径。可以为空

go env 查看环境变量,三个重要的变量GOPATH、GOROOT、GOBIN

vi /etc/profile.d/go.sh
GOPATH="~/goProject1/:~/goProject2/:~/goProject3/"
GOROOT="/usr/server/go"
GOOS=linux
PATH=$GOROOT/bin:$PATH

GOPATH目录结构

goProject // (goProject为GOPATH目录)

  -- bin  // golang编译可执行文件存放路径,可自动生成。
  -- pkg  // golang编译的.a中间文件存放路径,可自动生成。
  -- src  // 源码路径。按照golang默认约定,go run,go install等命令的当前工作路径(即在此路径下执行上述命令)

goProject // goProject 为GOPATH目录

  -- bin
     -- myApp1  // 编译生成
     -- myApp2  // 编译生成
     -- myApp3  // 编译生成
  -- pkg
  -- src
     -- common 1
     -- common 2
     -- common utils ...
     -- myApp1     // project1
        -- models
        -- controllers
        -- others
        -- main.go 
     -- myApp2     // project2
        -- models
        -- controllers
        -- others
        -- main.go 
     -- myApp3     // project3
        -- models
        -- controllers
        -- others
        -- main.go 

go开发编辑器

关于编辑器,推荐使用jetbrains出品的golang

第一个go程序

按照官网的demo,运行第一个go程序
新建hello.go文件

package main
import "fmt"
func main() {
    fmt.Println("Hello, 世界")
}

运行go可以有两种方式:
类似于python,php这样的脚本语言,直接运行,一步到位

$ go run hello.go

如java,c++ 一样,要进行编译,再运行编译输出的可执行文件

$ go build hello.go # 生成可执行文件 hello
$ ls
hello    hello.go
$ ./hello # 运行可执行文件,运行速度极快
Hello, 世界

    go是一门有着语法洁癖的语言,如强制规定了大括号的写法。长期以来,关于大括号是否要另起一行,是程序员界的圣战,且这个圣战跨越了语言,操作系统,编辑器。战斗双方多年来厮杀无数回合,不分胜负。python之父Guido van Rossum直接取消了大括号,完全采用缩进,绕过了圣战。与之相比,go就显得很霸道了,直接下了死命令: "大括号只能在当前行开始,不能另起一行,另起一行是异教徒,直接编译出错!" 让我们来试下。

格式规范

对于另起一行的强迫症程序员,想用go惟有屈服。此外,如果程序中有冗余变量,或引入了没有用到的包,都会报错

package main
import "fmt" // 引入了fmt包,但没有使用
func main() {
}

报错:

$ go build hello.go
# command-line-arguments
./hello.go:3:8: imported and not used: "fmt"
抛出错误

go的设计理念既简洁又严谨,用强制性的规则保证了代码的一致性。

命名规则

变量的命名规则:以字母或下划线开头,对大小写敏感。
Go内置关键字共25个

内置关键字

Go语言预定义标识符(区分大小写)共38个


预定义标识符

Go的注释方法

// :单行注释
/* */ :多行注释

Go程序的一般结构

//当前程序的包名
package main
//导入其它的包
import "fmt"
//全部变量的声明与赋值
var name = "gopher"
//一般类型的声明
type newType int
//结构的声明
type gopher struct{}
//接口的声明
type golang interface{}
//由main函数作为程序入口点启动
func main() {
  fmt.Println("Hello World!")
}

包的导入

别名:
import std "fmt"
省略调用(这样可以不用写包名,直接调用):
import . "fmt"
初始化,但不调用:
import _ "github.com/go-sql-driver/mysql"

可见性规则

只有首字母为大写的才能被其它包调用(类似面向对象里面的属性public 和private)

作用域

1、在代码块声明的变量,只在块内有效

代码块声明的变量

2、在函数内部声明的变量,只在函数内有效

函数内部声明的变

3、在函数外部声明的变量,在整个包内都有效。如果变量名称是大写的,即在整个程序中都有效

函数外部声明的变量

4、如果变量名要共享到其他包,就需要将包名改成大写,创建以下目录结构:

$ tree -L 2
├── main
│  └── main.go
└── test
   └── test.go

main.go

package main
import (
    "fmt"
    "../test" // 引入test包
)
func main() {
    fmt.Println(test.NAME) // 获取test包的NAME变量
}

test.go

package test
var NAME = "myname" // 变量名大写,才能被其他包引用,类似于java中的public关键字

运行结果

$ go run main/main.go
myname

你可以试下将test中的NAME改为name,go会报错,小写的变量是模块的私有变量,其他模块无法引用

常量与运算符

1.常量的定义
常量的值在编译时就已经确认
常量的定义格式与变量基本相同
等号右侧必须是常量或者常量表达式
常量表达式中的函数必须是内置函数
//定义单个常量

const a int = 1
const b = "A"
const (
    text = "123"
    length = len(text)
    num = b * 20
)
//同时定义多个变量
const i, j, k = 1, "2", "3"
const (
    text2 ,length2, num2 = "456", len(text2),  k* 10
)

2.常量的初始化规则
在定义常量组时,如果不提供初始值,则表示使用上行的表达式

var a = 1
const (
    b  = a
    //此时报错,因为全局变量在编译时不能确定值,常量必须是编译时能确定值的或使用内置函数获得的值(eg:len())
)
const (
    a = "A"
    b
    c
    //此时 a,b,c都为"A"
)
const (
    a, b = 1, "A"
    c, d
    //a,c为1,b,d为"A"。此时注意下行的常量个数必须与上行数量保持一致
)

3.常量的枚举
使用相同的表达式不代表具有相同的值
iota是常量的计数器,从0开始,组中每定义1个常量自动递增1
通过初始化规则与iota可以达到枚举的效果
每遇到一个const关键字,iota就会重置为0

const (
    a = "A"
    b
    c = iota
    d        //d的值为3,因为在常量组中,定义了4个变量所以iota计数器从0开始计数4次,所以为3
)

4.运算符

//优先级从高到低     
*  /  %  <<  >>  &  &^
=  -  |  ^   
==  !=  <  <=  >= >
&&
||

例子:

/*
6的二进制:0110       第一个
10的二进制:1011       第二个
---------
&  0010  = 2    (两个都是1才为1)
|  1111  = 15   (一个是1就是1)
^  1101  = 13   (两个只有一个是1才是1)
$^ 0100  = 4    (第二个为1则是0,否则与第一位相同)
*/

基础使用方法参考 以下链接


Python 和 Golang的不同与相似之处


变量

go在定义变量的规范上,有些反人类。c++, java 都是在变量名之前声明数据类型,而go却别出心裁的将数据类型声明置于变量名后面。
在Python 里面想建立一个变数的时候就直接建立

# -*- coding: UTF-8 -*-
 
counter = 100 # 赋值整型变量
miles = 1000.0 # 浮点型
name = "John" # 字符串

那么Golang 呢?在Golang 中变数分为几类:「新定义」、「预先定义」、「自动新定义」、「覆盖」。让我们来看看范例:

// 新定义:定义新的 a 变数为字串型别,而且值是「foo」
var a string = "foo"
// 预先定义:先定义一个新的 b 变数为字串型别但是不赋予值
var b string
// 自动新定义:让 Golang 依照值的内容自己定义新变数的资料型态
c := "bar"
// 覆盖:先前已经定义过 a 了,所以可以像这样直接覆盖其值
a = "fooooooo"

打印输出-Echo

在Python中你会很常用到Print来显示文字,像这样。

Python
print("Foo") // 輸出:Foo

A = "Bar"
print(A) // 輸出:Bar

B = "Hello"
print(B +", world!") // 輸出:Hello, world!

C = [1, 2, 3]
print(C) // 輸出:[1 2 3]

Golang
然而在Golang中你会需要fmt套件,关于「什么是套件」的说明你可以在文章下述了解。

fmt.Println("Foo") // 輸出:Foo

A := "Bar"  
fmt.Println(A) // 輸出:Bar

B := "Hello"  
fmt.Printf("%s, world!", B) // 輸出:Hello, world!

C := []int{1, 2, 3}  
fmt.Println(C) // 輸出:[1 2 3]

函数-Function

这很简单,而且两个语言的用法相差甚少,下面这是Python:
Python

# -*- coding:utf-8 -*-

def test():
    return "Hello, world!";
print(test())  // 輸出:Hello, world!  

Golang
只是Golang 稍微聒噪了一点,你必须在函式后面宣告他最后会回传什么资料型别。

func test() string {  
    return "Hello, world!"
}
fmt.Println(test()) // 輸出:Hello, world!

多值回传-Multiple Value

在Python 中你要回传多个资料你就会用上元组,然后将资料放入元组里面,像这样。
Python

# -*- coding:utf-8 -*-

def test():
    return ("YamiOdymel", 123456)
username, time = test()
print(username, time) #輸出:YamiOdymel 123456  

Golang
然而在Golang 中你可以不必用到一个阵列,函式可以一次回传多个值:

func test() (string, int) {  
    return "YamiOdymel", 123456
}
username, time := test()
fmt.Println(username, time) // 輸出:YamiOdymel 123456

匿名函式-Anonymous Function

两个语言的撰写方式不尽相同。

Python
在Python中,对匿名函数提供了有限支持。以map()函数为例,计算f(x)=x2时,除了定义一个f(x)的函数外,还可以直接传入匿名函数:

>>> map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
[1, 4, 9, 16, 25, 36, 49, 64, 81]
通过对比可以看出,匿名函数lambda x: x * x实际上就是:

def f(x):
    return x * x
# Python对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。

Golang

a := func() {  
    fmt.Println("Hello, world!")
}
a() // 輸出:Hello, world!

阵列(列表)-Array

Python
创建一个列表,只要把逗号分隔的不同的数据项使用方括号括起来即可。如下所示:

list1 = ['physics', 'chemistry', 1997, 2000]
list2 = [1, 2, 3, 4, 5 ]
list3 = ["a", "b", "c", "d"]

print(list2[0]) // 輸出:1  
print(list3[2]) // 輸出:c

与字符串的索引一样,列表索引从0开始。列表可以进行截取、组合等。

Golang

var a [2]string
a[0] = "foo"  
a[1] = "bar"

fmt.Println(a[0]) // 輸出:foo  

切片-Slice

可供「裁切」而且供自由扩展的列表。
slice() 函数实现切片对象,主要用在切片操作函数里的参数传递。

Python
格式: [start:end:step]
参数说明:
start -- 起始位置
stop -- 结束位置
step -- 间距

下实例展示了 slice 的使用方法:

>>>myslice = slice(5)    # 设置截取5个元素的切片
>>> myslice
slice(None, 5, None)
>>> arr = range(10)
>>> arr
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> arr[myslice]         # 截取 5 个元素
[0, 1, 2, 3, 4]

提取最后N个字符:

>>> letter = 'abcdefghijklmnopqrstuvwxyz'
>>> letter[-3:]
'xyz'

Golang

a := []string{"foo", "bar"}

fmt.Println(a[0]) // 輸出:foo  

我们刚才有提到你可以「切」他,记得吗?这有点像是PHP中的array_slice(),但是Golang直接让Slice「内建」了这个用法,其用法是:slice[开始:結束]。

p := []int{1, 2, 3, 4, 5, 6}

fmt.Println(p[0:1]) // 輸出:[1]  
fmt.Println(p[1:1]) // 輸出:[]  (!注意這跟 PHP 不一樣!)  
fmt.Println(p[1:])  // 輸出:[2, 3, 4, 5, 6]  
fmt.Println(p[:1])  // 輸出:[1]  

映照-Map

有键名和键值的阵列。

你可以把「映照」看成是一个有键名和键值的阵列,但是记住:「你需要事先定义其键名、键值的资料型态」,这仍限制你没办法在映照中存放多种不同型态的资料。

Python

data={}   # 先声明字典
data["username"] = "YamiOdymel";
data["password"] = "2016 Spring";
print(data["username"])  # 輸出:YamiOdymel  

以下实例展示了 map() 函数映射的使用方法:

map(function, iterable, ...)
>>>def square(x) :            # 计算平方数
...     return x ** 2
... 
>>> map(square, [1,2,3,4,5])   # 计算列表各个元素的平方
[1, 4, 9, 16, 25]
>>> map(lambda x: x ** 2, [1, 2, 3, 4, 5])  # 使用 lambda 匿名函数
[1, 4, 9, 16, 25]
 
# 提供了两个列表,对相同位置的列表数据进行相加
>>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
[3, 7, 11, 15, 19]

Golang
在Golang里可就没这么简单了,你需要先用make()宣告map。

data := make(map[string]string)
data["username"] = "YamiOdymel"  
data["password"] = "2016 Spring"

fmt.Println(data["username"]) // 輸出:YamiOdymel

接口-Interface

也许你不喜欢「接口」这个词,但用「介面」怕会误导大众,所以,是的,接下来会继续称其为「接口」。还记得你可以在Python 并不关心对象是什么类型,到底是不是鸭子,只关心行为。

Python
下面的代码就是一个简单的鸭子类型

#coding=utf-8
class Duck:
    def quack(self):
        print "Quaaaaaack!"
 
class Bird:
    def quack(self):
        print "bird imitate duck."
 
class Doge:
    def quack(self):
        print "doge imitate duck."
 
def in_the_forest(duck):
    duck.quack()
 
duck = Duck()
bird = Bird()
doge = Doge()
for x in [duck, bird, doge]:
    in_the_forest(x)

Golang
Golang 也一样并不关心对象是什么类型,只关心行为。!正因为Golang中的interface{}可以接受任何内容,所以你可以把它拿来存放任何型态的资料。

mixedData := []interface{}{"foobar", 123456}

mixedData2 := make(map[string]interface{})  
mixedData2["username"] = "YamiOdymel"  
mixedData2["time"]     = 123456

逆向处理-Defer

当我们程式中不需要继续使用到某个资源或是发生错误的时候,我们索性会将其关闭或是抛弃来节省资源开销,例如Python里的读取文档:
因此Golang中,defer通常用来释放函数内部变量,或者捕获错误发生。
Python 中,捕获错误发生使用try方法,内部变量释放使用gc模块释放,没有推迟执行

Python

try:
    with open('/etc/passwd') as f:
        for line in f:
            print(line)
except:
    #如果在读取文件的过程中,产生了异常,那么就会捕获到
    #比如 按下了 ctrl+c
    pass
finally:
    f.close()
    print('关闭文件')

Golang
在Golang中,你可以使用defer来在函式结束的时候自动执行某些程式(其执行方向为反向)。所以你就不需要在函式最后面结束最前面的资源。

defer可以被称为「推迟执行」,实际上就是在函式结束后会「反序」执行的东西,例如你按照了这样的顺序定义defer:A->B->C->D,那么执行的顺序其实会是D->C->B->A,这用在程式结束时还蛮有用的,让我们看看Golang如何改善上述范例。

handle := file.Open("example.txt")  
defer file.Close() // 关闭档案但「推迟执行」,所有程式结束后才会执行这裡

if errorA {  
    errorHandlerA()
}
if errorB {  
    errorHandlerB()
}

跳往-Goto

这东西很邪恶,不是吗?又不是在写BASIC,不过也许有时候你会在Python 用上呢。尽量不要使用。

Python
没有Goto方法,可以使用三方模块实现,但不推荐使用:
1、代码会很难看
2、Goto是一个很古老的方法

Golang

goto a  
fmt.Println("foo")

a:  
fmt.Println("bar") // 輸出:bar  

回环-Loops

Golang中仅有for一种回圈但却能够达成foreach、while、for多种用法。普通for回圈写法在两个语言中都十分相近。
Python中循环有While, 和for()

for循环
Python

for v in range(4,8):
    print(v)

Golang
在Golang请记得:如果你的i先前并不存在,那么你就需要定义它,所以下面这个范例你会看见i := 0。

for i := 0; i < 3; i++ {  
    fmt.Println(i) // 輸出 012
}

j := 0  
for ; j < 5 ; j++ {  
    fmt.Println(j) // 輸出:01234
}

foreach方式
Python里面可以使用range达成和一样的foreach方式。

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
fruits = ['banana', 'apple',  'mango']
for index in range(len(fruits)):
   print '当前水果 :', fruits[index]
 
print("Good bye!")

Golang里面虽然仅有for()但却可以使用range达成和一样的foreach方式。

data := []string{"a", "b", "c"}

for index, value := range data {  
    fmt.Printf("%d%s|", index, value)  // 輸出:0a|1b|2c|
}

for index := range data {  
    fmt.Printf("%d|", index)  // 輸出:0|1|2|
}

for _, value := range data {  
    fmt.Printf("%s|", value)  // 輸出:a|b|c|
}

重复-While
一个while(條件)回圈在PHP里面可以不断地执行区块中的程式,直到條件为false为止。
Python

count = 0
while count< 10:
    print("you are my sunshine "),
    count = count + 1

Golang
在Golang里也有相同的做法,但仍是透过for回圈,请注意这个for回圈并没有任何的分号(;),而且一个没有条件的for回圈会一直被执行。

i := 0
for i < 3 {  
    i++
    fmt.Println(i) // 輸出:123
}
for {  
    fmt.Println("WOW") // 輸出:WOWWOWWOWWOWWOW...
}

重复-Do While
Golang
在Golang中则没有相关函式,但是你可以透过一个无止尽的for回圈加上条件式来让他结束回圈。

i := 0
for {  
    i++
    fmt.Println(i) // 輸出:123

    if i > 2 {
        break
    }
}

可以在Golang中使用很邪恶的goto,实现LOOP。

i := 0
LOOP:  
    i++
    fmt.Println(i) // 輸出:123

    if i < 3 {
        goto LOOP
    }

抛出和捕捉异常-Try & Catch

也许你在Python中更常用的会是try .. Exception,在大型商业逻辑时经常看见如此地用法,实际上这种用法令人感到聒噪(因为你会需要一堆try区块):

PHP

# -*- coding: UTF-8 -*-

try:
    1 / 0
except Exception as e:
    '''异常的父类,可以捕获所有的异常'''
    print "0不能被除"
else:
    '''保护不抛出异常的代码'''
    print "没有异常"
finally:
    print "最后总是要执行我"

Golang
Golang中并没有try .. catch,实际上Golang也不鼓励这种行为(Golang推荐逐一处理错误的方式),倘若你真想办倒像是捕捉异常这样的方式,你确实可以使用Golang中另类处理错误的方式(可以的话尽量避免使用这种方式):panic(), recover(), defer

你可以把panic()当作是throw(丢出错误),而这跟PHP的exit()有87%像,一但你执行了panic()你的程式就会宣告而终,但是别担心,因为程式结束的时候会呼叫defer,所以我们接下来要在defer停止panic()

关于defer上述已经有提到了,他是一个反向执行的宣告,会在函式结束后被执行,当你呼叫了panic()结束程式的时候,也就会开始执行defer,所以我们要在defer内使用recover()让程式不再继续进行结束动作,这就像是捕捉异常。

recover()可以看作catch(捕捉),我们要在defer里面用recover()解决panic(),如此一来程式就会回归正常而不会被结束。

// 建立一個模仿 try&catch 的函式供稍後使用
func try(fn func(), handler func(interface{})) {  
    // 這不會馬上被執行,但當 panic 被執行就會結束程式,結束程式就必定會呼叫 defer
    defer func() { 
        // 透過 recover 來從 panic 狀態中恢復,並呼叫捕捉函式
        if err := recover(); err != nil {
            handler(err)
        }
    }()
    // 執行可能帶有 panic 的程式
    fn()
}

func foo(number int) {  
    if number < 10 {
        panic("number is less than 10")
    }
    if number > 10 {
        panic("number is greater than 10")
    }
}

func main() {  
    try(func() {
        foo(9)
    }, func(e interface{}) {
        fmt.Println(e) // 輸出:number is less than 10
    })

    try(func() {
        foo(11)
    }, func(e interface{}) {
        fmt.Println(e) // 輸出:number is greater than 10
    })
}

类别-Class

在Golang 中没有类别,但有所谓的「建构体(Struct)」和「接口(Interface)」,这就能够满足几乎所有的需求了,这也是为什么我认为Golang 很简洁却又很强大的原因。

让我们先用Python 建立一个类别,然后看看Golang 怎么解决这个问题。
在Golang 中没有类别,但有所谓的「建构体(Struct)」和「接口(Interface)」,这就能够满足几乎所有的需求了,这也是为什么我认为Golang 很简洁却又很强大的原因。
让我们先用Python 建立一个类别,然后看看Golang 怎么解决这个问题。
Python

# -*- coding: UTF-8 -*-
 
class Employee:
   '所有员工的基类'
   empCount = 0
 
   def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print "Total Employee %d" % Employee.empCount
 
   def displayEmployee(self):
      print "Name : ", self.name,  ", Salary: ", self.salary
 
"创建 Employee 类的第一个对象"
emp1 = Employee("Zara", 2000)
"创建 Employee 类的第二个对象"
emp2 = Employee("Manni", 5000)
emp1.displayEmployee()
emp2.displayEmployee()
print "Total Employee %d" % Employee.empCount

Golang
虽然Golang没有类别,但是「建构体(Struct)」就十分地堪用了,首先你要知道在Golang中「类别」的成员还有方法都是在「类别」外面所定义的,这跟PHP在类别内定义的方式有所不同,在Golang中还有一点,那就是他们没有public、private、protected的种类。

// 先定義一個 Foobar 建構體,然後有個叫做 a 的字串成員
type Foobar struct {  
    a string
}

// 定義一個屬於 Foobar 的 test 方法
func (f *Foobar) test () {  
    // 接收來自 Foobar 的 a(等同於 PHP 的 `$this->a`)
    fmt.Println(f.a)
}

b := &Foobar{a: "hello, world!"}  
b.test() // 輸出:hello, world!

相似的操作

日期-Date
切割字串-Split
关联阵列-Associative Array
是否存在-if a: 检测列表、字典元组是否存在
指针-Pointer
套件/汇入/汇出-Package / Import / Export

「Goroutine」和「Channel」是Golang的精髓,最重要的卖点

Go 是一种非常高效的语言,高度支持并发性。同时,它也像 C++和 Java 一样快。虽然相比于 Python 和 Ruby,使用 Go 建立东西需要更多的时间,但在后续的代码优化上可以节省大量时间。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容