c语言中i++和++i的区别

thbcm阅读(206)

今天有同学问C语言中 for 循环里那个 i++ 和 ++i 是否有区别,我告诉他在 for 循环中是没有区别的,现在具体说一下 i++ 和 ++i 的区别。

我们先用 while 语句写一下 i++:

for(i=1;i<10;i++)

int i=0;while (i<10){

printf("www.slyar.com");i++;

}

再用 while 语句写一下 ++i:

 for(i=1;i<10;++i)

int i=0;while (i<10){

printf("www.slyar.com");++i;

}

可以看到,最后i的值都是10,所以在 for 循环里,i++ 和 ++i 是没有区别的,那么区别在哪里呢?

现在我们再看一段程序:

#include<stdio.h>int main(){int i,x;

i=1;x=1;x=i++; //这里先让 X 变成 i 的值1,然后 i 加 1printf(“%d “,x);

i=1;x=1;x=++i; //这里先让 i 加 1,然后让 X 变成 i 的值 2printf(“%d “,x);

system(“pause”);return 0;}

试着运行一下这段程序,发现结果是 1 2 ,这就是 i++ 和 ++i 的区别了:

i++  :先引用后增加

++i  :先增加后引用

具体是什么意思呢?就是

i++  :先在 i 所在的表达式中使用 i 的当前值,后让 i 加 1

++i  :让 i 先加 1,然后在i所在的表达式中使用 i 的新值

我想这样说大家就应该明白了。。。

javascript中如何实现继承

thbcm阅读(195)

类式继承

//声明父类
//声明父类
function SuperClass() {
  this.superValue = true;
}
//为父类添加共有方法
SuperClass.prototype.getSuperValue = function () {
  return this.superValue;
};
​
//声明子类
function SubClass() {
  this.subValue = false;
}
​
//继承父类
SubClass.prototype = new SuperClass();
//为子类添加共有方法
SubClass.prototype.getSubValue = function () {
  return this.subValue;
};
​
var instance = new SubClass();
console.log(instance.getSuperValue()); //true
console.log(instance.getSubValue()); //false

类式继承需要将父类的实例赋值给子类原型, subClass.prototype 继承了 superClass。 这种类式继承有两个缺点。其一,由于子类通过原型 prototype 对父类实例化,继承了父类,父类中的共有属性要是引用类型,就会在子类中被所有实例共用,因此一个子类的实例更改子类原型从父类构造函数中继承来的共有属性就会直接影响到其他子类,如下:

function SuperClass() {
    this.courses = ['语文', '数学', '英语']
}
function SubClass() {}
SubClass.prototype = new SuperClass();
​
var instance1 = new SubClass()
var instance2 = new SubClass()
​
console.log(instance2.courses) //['语文', '数学', '英语']
instance1.courses.push('化学')
console.log(instance2.courses) //['语文', '数学', '英语', '化学']

instance1 的修改直接影响了 instance2,这是一个灾难的陷阱。其二,由于子类实现的继承是靠其原型 prototype 对父类的实例化实现的,因此在创建父类的时候,是无法向父类传递参数的,因而在实例化父类的时候也无法对父类构造函数内的属性进行初始化。如何解决这个问题?请继续往下看。

构造函数继承

function SuperClass(current) {
  this.courses = ["语文", "数学", "英语"];
  this.current = current;
}
​
//父类声明原型方法
SuperClass.prototype.getCourses= function () {
  console.log(this.courses);
};
​
//声明子类
function SubClass(current) {
  SuperClass.call(this, current);
}
​
var instance1 = new SubClass("语文");
var instance2 = new SubClass("数学");
​
instance1.courses.push('化学')
console.log(instance1.courses); //["语文", "数学", "英语", "化学"]
console.log(instance1.current); //语文
console.log(instance2.courses); //["语文", "数学", "英语"]
console.log(instance2.current); //数学
​
instance1.getCourses() //TypeError: instance1.getCourses is not a function

SuperClass.call(this, current) 这条语句是构造函数继承的精华。由于 call 这个方法可以更改函数的作用环境,因此在子类中,对 SuperClass 调用这个 call 就是将子类中变量在父类中执行一遍,由于父类中是给 this 绑定属性的,因此子类也就继承了父类的共有属性。 由于这种类型的继承没有涉及原型 prototype,所以父类的的原型方法不会被子类继承,要想被子类继承,只能将 showCourse 放在父类构造函数中,但是这样就违背了代码复用的原则。为了综合以上两种继承的优点,于是有了组合继承

组合继承

//组合继承
​
function SuperClass(current) {
  //引用类型共有属性
  this.courses = ["语文", "数学", "英语"];
  // 值类型共有属性
  this.current = current;
}
​
SuperClass.prototype.getCourses = function () {
  console.log(this.courses);
};
​
SuperClass.prototype.getCurrent = function () {
  console.log(this.current);
};
​
// 声明子类
function SubClass(current, time) {
  //构造函数继承父类属性
  SuperClass.call(this, current);
  this.time = time;
}
//类式继承 子类原型继承父类
SubClass.prototype = new SuperClass();
//子类原型方法
SubClass.prototype.getTime = function () {
  console.log(this.time);
};

在子类构造函数中执行父类构造函数,在子类的原型上实例化父类就是组合模式,子类实例中更改父类继承下来的引用类型属性 courses 不会改变其他实例,测试如下

var instance1 = new SubClass("语文", "9:00");
instance1.getTime(); //9:00
instance1.courses.push('化学')
instance1.getCourses(); //["语文", "数学", "英语", "化学"]
instance1.getCurrent(); //语文
console.log(instance1.current)//语文
​
var instance2 = new SubClass("数学", "10:00");
instance2.getTime(); //10:00
instance2.getCourses(); //["语文", "数学", "英语"]
instance2.getCurrent(); //数学
console.log(instance2.current)//数学

但是该模式在执行子类构造函数时执行了一遍父类函数,在实现子类原型继承时又执行了一遍父类构造函数,调用了父类构造函数两遍,显然是一个设计缺陷,那还有更好的方式吗?针对该缺陷,出现了“寄生组合式继承”

寄生组合式继承

在介绍这种继承方式之前,需要先了解 “原型式继承”和“寄生式继承”

基础了解

原型式继承

function inheritObject(o) {
  function F() {}
  F.prototype = o;
  return new F();
}
​
var course = {
  name: "语文",
  alikeCourse: ["数学", "英语"],
};
​
var newCourse = inheritObject(course);
newCourse.name = "化学";
newCourse.alikeCourse.push("物理");
​
var otherCourse = inheritObject(course);
otherCourse.name = "政治";
otherCourse.alikeCourse.push("历史");
​
console.log(newCourse.name); //化学
console.log(newCourse.alikeCourse); //["数学", "英语", "物理", "历史"]
​
console.log(otherCourse.name); //政治
console.log(otherCourse.alikeCourse); //["数学", "英语", "物理", "历史"]
​
console.log(course.name); //语文
console.log(course.alikeCourse); //["数学", "英语", "物理", "历史"]

inheritObject 可以看做是对类式继承的一种封装,其中的过度类 F 相当于类式继承中的子类。在类式继承中存在的共用引用类型的问题依然存在,但是过度类 F 构造函数中无内容,所以开销较小。

寄生式继承

“寄生式继承”是在“原型式继承”的基础上继续增强。

function inheritObject(o) {
  function F() {}
  F.prototype = o;
  return new F();
}
​
var course = {
  name: "语文",
  alikeCourse: ["数学", "英语"],
};
​
function createCourse(obj) {
  //通过原型继承方式创建新对象
  var o = new inheritObject(obj);
  // 拓展新对象
  o.getName = function () {
    console.log(this.name);
  };
  return o;
}
​
const newCourse = createCourse(course)

这种方式在某个对象内部持续增长属性,像寄生式生长,所以称之为寄生继承。寄生继承是对原型继承的二次封装,并且在二次封装的过程中对继承的对象做了拓展,这样新创建的对象不仅仅有父类中的属性和方法,而且还添加了新的属性和方法。在这种思想的基础上,结合组合式继承,衍生了 “寄生组合式继承”

实现

function inheritObject(o) {
    function F() {}
    F.prototype = o;
    return new F();
  }
function inheritPrototype(subClass, superClass) {
    //复制一份父类的原型副本保存在变量中
    var p = inheritObject(superClass.prototype)
    //修正因为重写子类原型导致子类的constructor属性被修改
    p.constructor = subClass
    //设置子类的原型
    subClass.prototype = p
}

以上父类原型保存一个副本,赋值给子类原型,从而实现继承,且并未重新调用一次父类函数,测试如下,与组合继承模式相近

//test
​
function SuperClass(current) {
  //引用类型共有属性
  this.courses = ["语文", "数学", "英语"];
  // 值类型共有属性
  this.current = current;
}
​
SuperClass.prototype.getCourses = function () {
  console.log(this.courses);
};
​
SuperClass.prototype.getCurrent = function () {
  console.log(this.current);
};
​
// 声明子类
function SubClass(current, time) {
  //构造函数继承父类属性
  SuperClass.call(this, current);
  this.time = time;
}
​
//寄生式继承 子类原型继承父类
inheritPrototype(SubClass, SuperClass);
​
//类式继承 子类原型继承父类
// SubClass.prototype = new SuperClass();
​
//子类原型方法
SubClass.prototype.getTime = function () {
  console.log(this.time);
};
​
var instance1 = new SubClass("语文", "9:00");
var instance2 = new SubClass("数学", "10:00");
​
instance1.getTime(); //9:00
instance1.courses.push("化学");
instance1.getCourses(); //["语文", "数学", "英语", "化学"]
instance1.getCurrent(); //语文
console.log(instance1.current); //语文
​
instance2.getTime(); //10:00
instance2.getCourses(); //["语文", "数学", "英语"]
instance2.getCurrent(); //数学
console.log(instance2.current); //数学

区别仅在

//寄生式继承 子类原型继承父类
inheritPrototype(SubClass, SuperClass);
​
//类式继承 子类原型继承父类
// SubClass.prototype = new SuperClass();

从而实现多个子类多个实例不相互影响,父类构造函数仅仅调用一次,堪当 JavaScript 继承的终极模式。

C语言和C#的区别

thbcm阅读(189)

C#是三大主流OOP(面向对象编程)语言(C++,Java,C#)之一,它与C之间的一些差别。

C语言诞生得非常之早,C语言的目标就是比汇编方便易用,同时不要损失汇编的表达能力。所以 C 语言可以看成是“高级的汇编”语言。C语言的源代码基本上可以非常容易地对应到汇编代码,而且可以不需要什么运行时环境的支持。C的特点,简单容易编译,灵活贴近底层。所以一直到现在,一些需要直接和硬件打交道的软件都还是用 C 语言写的。总之 C 语言编写简单,更接近底层,直观得管理数据存储。

C#语言抽象层次高且基本只有一种(面向对象的),运行时支持丰富(垃圾回收等),类库丰富。所以它就是好学,易用,同时兼顾运行效率,尽量优化。C# 与 Java 类似,编译后得到的还不是机器代码,而是运行在虚拟机中的元指令。它对安全性做了更多的考虑,没有指针,不能直接操作内存,自动实现内存管理。

最直观的区别就是:C# 没有指针类型,内存自动管理;C# 有字符串类型,C 语言没有,靠字符数组或指针来存字符串;C# 中 switch 后可跟 string 类型,并且会禁止所有 switch..case 语句的失败情形,除非 case 语句后是空格,否则执行了前一个case语句就算没有 break 也会停止执行后面的 case 语句; C# 中定义数组时,数组长度可以是变量 ,而 C 语言中定义时数组长度只能是一个常量表达式,动态分配内存需要用到 molla 函数;C 语言中没有集合类型;C# 有 foreach 可用于数组和集合的遍历等。

总的来说,C 语言更底层,很多东西都需要自己 DIY,但极其灵活,功能十分强大,其精华在于指针,直接管理数据存储,面向过程编程,很多操作系统和系统软件都是用 C 语言写的;C# 则更容易上手,很多东西都已经写好,直接使用即可,避免了内存的直接管理,面向对象编程;正如一个冷笑话所讲:“C 语言:指针最好用。 C++:最好不用指针。 C#:指针是什么?”

=================================================

一、难易程度的区别。

c# 属于 .net framework 中的一个产品,简单易用,但开发出来的东西目前还需要安装运行库才能供别人使用。c 语言是一种古老难用的语言,目前可能在嵌入式系统用的比较多,另外大学会有这门课。相关的还有 c++,可以做系统的底层开发,也不容易掌握。

二、内容上的区别。

C 语言面向过程,开发非托管程序,编译成 exe是二进制可执行文件,不可跨平台。C# 面向对象,开发托管程序,编译成 exe 是中间语言,需要在 .NET 平台上进行二次动态编译,之后才能执行,可跨平台。

三、特点上的区别。

C 语言诞生得非常早,当时人们普遍还习惯用汇编语言编写软件,而且没有什么统一,通用的操作系统,基本上软件都是从0开始写的。C# 是一种安全的、稳定的、简单的、优雅的,由 C 和 C++ 衍生出来的面向对象的编程语言。它在继承 C 和 C++ 强大功能的同时去掉了一些它们的复杂特性。

===========================================================

C:面向过程,语法太麻烦,但对硬件的底层编程和对内存的管理的灵活性方面c是其他高级语言所不可及的。

C#:纯面向对象的(跟 java 很像),是 ms .net framework 的主力之一,它的代码运行是安全的,里面没有指针和引用,像 java 一样有垃圾回收机制。

语法基本没有区别,首先 C# 不必对指针进行太多的研究,然后可遗址性等,其它的区别相当大。可以说不是一个方向的。

开发环境跟开发语言也是两个不同的概念

学习 C# 并不必须有 C 语言的基础,不过,如果你学过 C 语言,那会事半功倍的,因为他们之间有很多语法是一样的。作为初学者,并没有必要先去学习 C 语言,你只需要有 C# 的完整的教程就行了。

在这里推荐基本C#好课:C#入门手册C#微课

C语言中%*s中*是什么作用?

thbcm阅读(162)

取决于在 ​scanf​ 中使用还是在 ​printf​ 中使用。

  1. 在 ​scanf​ 中使用,则添加了​*​的部分会被忽略,不会被参数获取。例如:
    int a,b;char b[10];
    scanf("%d%*s",&a,b);

    输入为:​12 abc​ 那么​12​将会读取到变量 ​a​ 中,但是后面的 ​abc​ 将在读取之后抛弃,不赋予任何变量(例如这里的字符数组​b​)

  2. 在​ printf​ 中使用,表示用后面的形参替代的位置,实现动态格式输出。例如:
    printf("%s", 10, s);
    /意思是输出字符串 s,但至少占10个位置,不足的在字符串s左边补空格,这里等同于 printf("%10s", s);*/
  3. 在举个例子,假如要打印 linux 根文件系统下的​ /proc/x/status​ 中的第一行 “​Name: login​”,如下
[root@sz /proc/898]#cat status 
Name:   login
State:  S (sleeping)
Tgid:   898
Pid:    898
PPid:   519
TracerPid:      0
Uid:    0       0       0       0
Gid:    0       0       0       0
FDSize: 32
Groups:
VmPeak:     1232 kB
VmSize:     1232 kB
VmLck:         0 kB
VmPin:         0 kB
VmHWM:       312 kB
VmRSS:       312 kB
VmData:       64 kB
VmStk:       136 kB
VmExe:       708 kB
VmLib:       312 kB
VmPTE:         8 kB
VmSwap:        0 kB
Threads:        1
SigQ:   0/469
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000000
SigCgt: 0000000000002000
CapInh: 0000000000000000
CapPrm: 0000001fffffffff
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff
Cpus_allowed:   1
Cpus_allowed_list:      0
Mems_allowed:   1
Mems_allowed_list:      0
voluntary_ctxt_switches:        1
nonvoluntary_ctxt_switches:     2

为了只获取 “​Name: login​”中的 ​login​,可以采用如下

//描述: 线程是否存在
//返回: 成功表示存在,返回true,反之为false。
bool IsThreadExist(char *task_name)
 {
     DIR *dir;
     struct dirent *ptr;
     FILE *fp;
     char filepath[50];
     char cur_task_name[50];
     char buf[BUF_SIZE];
     bool fRet = false;
     dir = opendir("/proc"); 
     if (NULL != dir)
     {
         while ((ptr = readdir(dir)) != NULL)
         {
             if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0))
                 continue;
             if (DT_DIR != ptr->d_type)
                 continue;
             sprintf(filepath, "/proc/%s/status", ptr->d_name);
             fp = fopen(filepath, "r");
             if (NULL != fp)
             {
                 if( fgets(buf, BUF_SIZE-1, fp)== NULL ){
                     fclose(fp);
                     continue;
                 }
                 sscanf(buf, "%*s %s", cur_task_name);
                 if (strcmp(task_name, cur_task_name) == 0){
                     fRet = true;
                 }
                 fclose(fp);
             }
         }
         closedir(dir);
     }
     return fRet;
 }

面向对象编程及其三大特性

thbcm阅读(159)

编程语言分为面向过程编程、函数式编程和面向对象编程。其实 python 就是一种面向对象编程,那么我们先了解一下它们的特点和优缺点以及它们的区别是什么。

面向过程编程:“面向过程”(Procedure Oriented)是一种以过程为中心的编程思想。这些都是以什么正在发生为 目标进行编程,不同于面向对象的是谁在受影响。与面向对象明显的不同就是封装、继承、类。

面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能,开发过程的思路是将数据与函数按照执行的逻辑顺序组织在一起,数据与函数分开考虑。

  • 特性:模块化 流程化
  • 优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发,性能是最重要的因素。
  • 缺点:没有面向对象易维护、易复用、易扩展

函数式编程: 函数式编程也是种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),而且 λ 演算的函数可以接受函数当作输入(参数)和输出(返回值)。

它的主要思想是把运算过程尽量写成一系列嵌套的函数调用。

Python 不是也不大可能会成为一种函数式编程语言,但是它支持许多有价值的函数式编程语言构建。也有些表现得像函数式编程机制但是从传统上也不能被认为是函数式编程语言的构建。

Python内建函数 : filter()、map()、reduce()、max()、min()

面向对象编程:面向对象是按人们认识客观世界的系统思维方式,采用基于对象(实体)的概念建立模型,模拟客观世界分析、设计、实现软件的办法。通过面向对象的理念使计算机软件系统能与现实世界中的系统一一对应。

面向对象编程可以将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程。

  • 特性:抽象 封装 继承 多态
  • 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合 的系统,使系统更加灵活、更加易于维护
  • 缺点:性能比面向过程低

可以拿生活中的实例来理解面向过程与面向对象,例如五子棋,面向过程的设计思路就是首先分析问题的步骤:1、开始游戏,2、黑子先走,3、绘制画面,4、判断输赢,5、轮到白子,6、绘制画面,7、判断输赢,8、返回步骤2,9、输出最后结果。把上面每个步骤用不同的方法来实现。

如果是面向对象的设计思想来解决问题。面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为1、黑白双方,这两方的行为是一模一样的,2、棋盘系统,负责绘制画面,3、规则系统,负责判定诸如犯规、输赢等。第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。

可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了多个步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。

面向对象编程三大特性

在看面向对象编程三大特性之前,先了解一下对象和类的区别。

对象和类

类(Class)是现实或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。类实际上是创建实例的模板。

对象(Object)是具有类类型的变量。类和对象是面向对象编程技术中的最基本的概念。 而对象就是一个一个具体的实例。

定义类的方法:

class 类(): pass

那么如何将类转换成对象呢?

实例化是指在面向对象的编程中,把用类创建对象的过程称为实例化。是将一个抽象的概念类,具体到该类实物的过程。实例化过程中一般由类名 对象名 = 类名(参数1,参数2…参数n)构成。

定义类之后一般会用到构造方法 init 与其他普通方法不同的地方在于,当一个对象被创建后,会立即调用构造方法(也称为魔术方法)。自动执行构造方法里面的内容。

1.封装特性

封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。 所以,在使用面向对象的封装特性时,需要:

  1. 将内容封装到某处
  2. 从某处调用被封装的内容,调用方法如下:
  • 通过对象直接调用被封装的内容: 对象.属性名
  • 通过 self 间接调用被封装的内容: self.属性名
  • 通过 self 间接调用被封装的内容: self.方法名()

对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过 对象直接或者 self 间接获取被封装的内容。

具体例子如下:

# 1). 类的定义
class People:
   # 构造方法(魔术方法): 当创建对象时会自动调用并执行;
   # self实质上是实例化出来的对象, e.g: xiaoming, xiaohong;
   def __init__(self, name, age, gender):
      # print("正在创建对象")
       # print(self) # 实质上是一个对象
       # 将创建对象的属性(name, age, gender)封装到self(就是实例化的对象)变量里面;
       # 在类里面定义的变量: 属性
       self.name = name
       self.age = age
       self.gender = gender
   # 在类里面定义的函数: 方法
   def eat(self):
       print('%s eating......' %(self.name))
   def sleep(self):
       # 获取对象self封装的属性(name, age, gender)
       print('%s sleep.......' %(self.name))
# 2). 实例化: 通过类实现对象的创建
xiaoming = People("小明", 20, '男')
# 将对象self/xiaoming封装的属性(name, age, gender)从里面拿出来;
print(xiaoming.name)
xiaoming.eat()
xiaoming.sleep()
xiaohong = People("小红", 20, '女')
print(xiaohong.name)
xiaohong.eat()
xiaohong.sleep()

2、继承特性

1)继承

继承描述的是事物之间的所属关系,当我们定义一个 class 的时候,可以从某个现有的 class 继承,新的 class 称为子类、扩展类(Subclass),而被继承的class称为基类、父类或超类(Baseclass、Superclass)。

问题一:如何实现继承呢?

子类在继承的时候,在定义类时,小括号( )中为父类的名字,例如: class son(father): 这里father就是son的父类。

问题二:继承的工作机制是什么?

父类的属性、方法,会被继承给子类。 举例如下:如果子类没有定义 init 方法,父类有,那么在子类继承父类的时候这个方法就被继承了,所以只要创建对象,就默认执行了那个继承过来的 init 方法。

重写父类方法:就是在子类中,有一个和父类相同名字的方法,那么在子类中的方法就会覆盖掉父类中同名的方法,就实现了对父类方法的重写。

调用父类的方法:

  1. 在子类中,直接利用 父类名.父类的方法名()
  2. super() 方法: python2.2+的功能,格式为: super(子类名称, self).父类的方法名() (建议用此方法)

2)多继承

多继承,即子类有多个父类,并且具有它们的特征。

新式类与经典类的区别:

在 Python 2及以前的版本中,由任意内置类型派生出的类,都属于“新式类”,都会获得所有“新式类”的特性;反之,即不由任意内置类型派生出的类,则称之为“经典类”。

新式类:

class 类名(object):

pass

经典类:

class 类名:

pass

“新式类”和“经典类”的区分在 Python 3之后就已经不存在,在 Python 3.x之后的版本,因为所有的类都派生自内置类型 object(即使没有显示的继承 object 类型),即所有的类都是“新式类”。

它们两个最明显的区别在于继承搜索的顺序不同,即:

经典类多继承搜索顺序(深度优先算法):先深入继承树左侧查找,然后再返回,开始查找右侧。

新式类多继承搜索顺序(广度优先算法):先在水平方向查找,然后再向上查找。

图示如下:

具体例子如下:

# python2:
    # 经典类:  class Father:
    # 新式类:   class Father(object):
# python3:
#       所有的都是新式类
# 新式类:  广度优先
# 经典类:  深度优先
class D:
    a = 'd'
class B(D):
    pass
class C(D):
    a = 'c'
class A(B, C):
    pass
obj = A()
print(obj.a)

运行结果是 c,由此可见为广度优先搜索的结果。

3)私有属性与私有方法

默认情况下,属性在 Python 中都是“public”, 大多数 OO (面向对象)语言提供“访问控制符”来限定成员函数的访问。

在 Python 中,实例的变量名如果以 __ (双下划线)开头,就变成了一个私有变量/属性(private),实例的函数名如果以 __ 开头,就变成了一个私有函数/方法(private)只有内部可以访问,外部不能访问。

那么私有属性一定不能从外部访问吗?

python2版本不能直接访问 属性名,是因为 Python 解释器对外把 属性名 改成了 _类名属性名 ,所以,仍然可以通过 _类名属性名 来访问 属性名 。 但是不同版本的 Python 解释器可能会把 属性名 改成不同的变量名,因此不建议用此类方法访问私有属性。

私有属性和方法的优势:

  1. 确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
  2. 如果有要允许外部代码修改属性怎么办?可以给类增加专门设置属性方法。 为什么大费周折?因为在方法中,可以对参数做检查,避免传入无效的参数。

具体实例如下:

class Student(object):
    __country = 'china'
    def __init__(self, name, age, score):
        self.name = name
        # 年龄和分数是私有属性
        self.__age = age
        self.__score = score
    # 私有方法, 只能在类内部执行;
    def __modify_score(self, scores):
        self.__score = scores
        print(self.__score)
    def set_age(self, age):
        if 0<age <150:
            self.__age = age
            print("当前年龄:", self.__age)
        else:
            raise  Exception("年龄错误")
    def set_score(self, scores):
        if len(scores) == 3:
            self.__score = scores
        else:
            raise Exception("成绩异常")
class MathStudent(Student):
    pass
student = Student("Tom", 10, [100, 100, 100])
# 在类的外部是不能直接访问的;
print(student.name)
student.set_age(15)
# Python 解释器对外把  __属性名 改成了  _类名__属性名
# print(student._Student__age)

3.多态特性

多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。通俗来说: 同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

多态的好处就是,当我们需要传入更多的子类,只需要继承父类就可以了,而方法既可以直接不重写(即使用父类的),也可以重写一个特有的。这就是多态的意思。调用方只管调用,不管细节,而当我们新增一种的子类时,只要确保新方法编写正确,而不用管原来的代码。这就是著名的“开放封闭”原则:

  • 对扩展开放(Open for extension):允许子类重写方法函数
  • 对修改封闭(Closed for modification):不重写,直接继承父类方法函数

初学java开发用什么工具好

thbcm阅读(165)

今天为什么要说一下初学Java用什么工具进行编辑最好。这是因为现在很多初学Java的学生都喜欢用有提示功能的编程软件。这样虽然会更加方便和快捷,但是相对的,你的基础知识却并不是那么牢靠。本来初学者就是打基础的阶段。只有把“地基”打好了,才能更好的“建楼”。下面和大家说说初学Java开发实用的工具。

1、文本文档

  文本文档,其实就是记事本,有没有觉得不可思议呢?其实最早的编程方式,就是在文本文档里编写的。编程里面的所有代码文件,都可以用修改文本文档的扩展名得到,用这样的方式编程,可以让你清楚的知道和理解程序到底是个什么东西,其根本的运行生成原理是怎样的。缺点就是工作量大,出了问题不好找原因,对语法要求准确,因为写错了不好找。而现在,因为编程工具的出现,让编程变得容易,所以现在不再有这样的教学,一般都是直接告诉你如何使用工具开发,在如今,用文本文档学编程,绝对称得上是最嚣张的学习方式。建议有点基础的可以去试试,对于理解程序绝对有好处。

2、eclipse

 

 

  Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。同时,Eclipse 附带了一个标准的插件集,包括 Java 开发工具(JavaDevelopmentKit,JDK)。电脑下载后只需要简单的配置就可以使用,是目前企业用的比较多的一种Java软件开发工具,作为工具的优点,就是可以提高程序的编写,也便于调试和查找错误,对于初学者来说,可以减少编程的误漏,能够快速直观的显示出软件的结果,还可以逐步调试让你理解软件的运行机制,缺点就是可能你学会了使用它去实现功能,但对于程序的底层了解会有所缺失。 

 

3、myeclipse

 

 

  MyEclipse 是在 eclipse 基础上加上自己的插件开发而成的功能强大的企业级集成开发环境,主要用于 Java、JavaEE 以及移动应用的开发。MyEclipse 的功能非常强大,支持也十分广泛,尤其是对各种开源产品的支持相当不错。这款软件是在 eclipse 上升级而来的,在很多地方还简化了程序的编写以及环境的配置,对于开发人员来说,是一种很好很强大的软件开发工具。对于初学来说,可以直接体验到企业级的开发方式,有利于快速上手做项目,对于底层的一些机制就会忽略很多,程序是一门应用型技术,从实现功能上来说, myeclipse 算是 Java 工具里面的翘楚,如果要快速培养兴趣,这款工具是很适合的。

 

4、其他的一些辅助工具

  还有一些其他的辅助开发工具,例如 Tomcat 服务器,它是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。学 web 编程必用的工具软件,只需要简单配置即可使用。

  其他的一些工具软件如测试集成等辅助软件,网上挺多的,初学 Java,一般建议用 myeclipse,上手快,使用简单,稍熟悉一些可以自己用文本体验一下加深理解,当然,工具只是工具,能不能学好学精,主要还是看你够不够努力。

Java编程需要必须掌握哪些基础知识?

thbcm阅读(161)

Java编程基础知识学习是每一个程序猿进入编程领域的人所必须经历的学习过程,只有基本功修炼好,才能有更深远更好的的发展,那Java编程基础知识有哪些,需要必须掌握的呢?

1、掌握静态方法和属性  

静态方法和属性用于描述某一类对象群体的特征,而不是单个对象的特征。Java 中大量应用了静态方法和属性,这是一个通常的技巧。但是这种技巧在很多语言中不被频繁地使用。

理解静态方法和属性对于理解类与对象的关系是十分有帮助的,在大量的  Java 规范中,静态方法和属性被频繁使用。因此学习者应该理解静态方法和属性。Java 在方法和属性的调用上是一致的,区别只表现在声明的时候,这和 C++ 是不同的。

2、重视接口

在面向对象早期的应用中大量使用了类继承。随着软件工程理论的不断发展,人们开始意识到了继承的众多缺点,开始努力用聚合代替继承。软件工程解决扩展性的重要原则就是抽象描述,直接使用的工具就是接口。接口近年来逐渐成为 Java 编程方法的核心。

另一方面,就应用而言,大部分开发是建立在规范基础之上的,不需要自己建立复杂的继承关系和庞大的类。因此读懂规范和用好规范已经成为应用程序开发人员的首要任务,Java 各项规范的主要描述手段就是接口。

3、学好集合框架

Java描述复杂数据结构的主要方式是集合框架。Java没有指针,而是通过强大的集合框架描述数组、对象数组等复杂的数据结构。学好这些数据结构的描述方法对于应用程序编写,特别是涉及到服务器方、3层结构编程至关重要。程序员在这个时候不能再用诸如数据库结果集之类的结构描述数据了。

由于很多语言没有这么强大的集合框架体系,很多初学者不知所措,更不知道拿来做什么用,因此应该引起足够的重视。

4、例外捕捉

Java 对例外捕捉的强调是空前的,它强迫程序员用显着的与逻辑方法完全不同的方式描述例外捕捉,对于程序描述的完整性和严谨性有很大的意义。c++ 也有类似的机制,但是我们看到很多 c++ 程序员并不习惯使用这些机制。Java 的初学者应该充分学习好这种例外捕捉机制,养成良好的编程习惯。

5、多线程需要理解机理

很多Java程序员热衷于多线程程序编写,认为是对逻辑能力的挑战。其实在大量应用中根本就不需要编写多线程程序,或者说大多数编写应用程序的程序员不会去写多线程程序。这是因为多线程机制都内置到基础平台当中了。

程序员应该了解的是多线程原理和多线程安全,这对于今后准确地把握程序是至关重要的。例如 JSP 中编写到不同的位置对于多个用户环境的安全影响完全不同,又如着名的Super Servlet是每一个访问作为一个进程,但是每一个页面是一个线程,和 Servlet 正好相反,对程序的性能和安全的影响有天壤之别。

6、了解网络编程

Java号称是最强的网络编程语言,但是大多数应用程序开发人员是从来不会自己开发什么底层的网络程序的。

需要做只是了解原理就够了。网络机制的实现是靠平台实现的,除非自己开发平台,否则是不需要知道socket怎么实现,怎么监听访问的。因此在这方面花太多的功夫就偏离了”将来的应用开发是在成熟的平台上展开,而不是自己从底层开发平台”这一假设。

python为什么叫爬虫?

thbcm阅读(175)

爬虫通常指的是网络爬虫,就是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。因为 python 的脚本特性,python 易于配置,对字符的处理也非常灵活,加上 python 有丰富的网络抓取模块,所以两者经常联系在一起。

在进入文章之前,我们首先需要知道什么是爬虫。爬虫,即网络爬虫,大家可以理解为在网络上爬行的一只蜘蛛,互联网就比作一张大网,而爬虫便是在这张网上爬来爬去的蜘蛛,如果它遇到自己的猎物(所需要的资源),那么它就会将其抓取下来。比如它在抓取一个网页,在这个网中他发现了一条道路,其实就是指向网页的超链接,那么它就可以爬到另一张网上来获取数据。不容易理解的话其实可以通过下面的图片进行理解:

因为 python 的脚本特性,python 易于配置,对字符的处理也非常灵活,加上 python 有丰富的网络抓取模块,所以两者经常联系在一起。Python 爬虫开发工程师,从网站某一个页面(通常是首页)开始,读取网页的内容,找到在网页中的其它链接地址,然后通过这些链接地址寻找下一个网页,这样一直循环下去,直到把这个网站所有的网页都抓取完为止。如果把整个互联网当成一个网站,那么网络蜘蛛就可以用这个原理把互联网上所有的网页都抓取下来。

作为一门编程语言而言,Python 是纯粹的自由软件,以简洁清晰的语法和强制使用空白符进行语句缩进的特点从而深受程序员的喜爱。举一个例子:完成一个任务的话,c 语言一共要写1000行代码,java 要写100行,而 python 则只需要写20行的代码。使用 python来完成编程任务的话编写的代码量更少,代码简洁简短可读性更强,一个团队进行开发的时候读别人的代码会更快,开发效率会更高,使工作变得更加高效。

这是一门非常适合开发网络爬虫的编程语言,而且相比于其他静态编程语言,Python 抓取网页文档的接口更简洁;相比于其他动态脚本语言,Python 的 urllib2 包提供了较为完整的访问网页文档的 API。此外,python 中有优秀的第三方包可以高效实现网页抓取,并可用极短的代码完成网页的标签过滤功能。

python爬虫的构架组成如下图:

1、URL 管理器:管理待爬取的 url 集合和已爬取的 url 集合,传送待爬取的 url 给网页下载器;

2、网页下载器:爬取url对应的网页,存储成字符串,传送给网页解析器;

3、网页解析器:解析出有价值的数据,存储下来,同时补充 url 到 URL 管理器。

而 python 的工作流程则如下图:

(Python 爬虫通过 URL 管理器,判断是否有待爬 URL,如果有待爬 URL,通过调度器进行传递给下载器,下载 URL 内容,并通过调度器传送给解析器,解析URL内容,并将价值数据和新 URL 列表通过调度器传递给应用程序,并输出价值信息的过程。)

Python 是一门非常适合开发网络爬虫的编程语言,提供了如 urllib、re、json、pyquery 等模块,同时又有很多成型框架,如 Scrapy 框架、PySpider 爬虫系统等,本身又是十分的简洁方便所以是网络爬虫首选编程语言!

推荐好课:Python3入门Python3进阶

Python中openpyxl模块基本用法

thbcm阅读(166)

openpyxl 是一个用于处理 xlsx 格式 Excel 表格文件的第三方 python 库,其支持 Excel 表格绝大多数基本操作。

安装方法

使用 pip 或通过专门 python IDE(如pyCharm)进行安装

其中pip安装方法,命令行输入: pip install openpyxl

基本使用

第一步先是要导入 openpyxl 模块

import openpyxl

读取Excel文档

通过调用方法 load_workbook(filename) 进行文件读取,该方法中还有一个 read_only 参数用于设置文件打开方式,默认为可读可写,该方法最终将返回一个 workbook 的数据对象

# 文件必须是xlsx格式,如果是其他格式在执行前可利用win32辅助转化
wb = openpyxl.load_workbook(‘example.xlsx’)

(一)获取工作表

每一个 Excel 表格中都会有很多张 sheet 工作表,在对表格操作前需要先选定一张工作表

# 获取所有工作表名(返回一个列表)
sheets = wb.get_sheet_names()

# 获取某一特定的工作表
sheet = wb.get_sheet_by_name('Sheet2')

# 获取工作表的表名
sheet_name = sheet.title

# 一般来说,表格大多数用到的是打开时显示的工作表,这时可以用active来获取当前工作表
sheet = wb.active

(二)获取单元格

对 Excel 表格的操作最终都落于对单元格的操作,获取单元格有两种获取方法:sheet[列行名]和 sheet.cell(row,column)

# 通过sheet[列行名]获取
a = sheet['A2']

# 通过sheet.cell(row,column)获取
b = sheet.cell(1, 2)  # 即sheet['B1']

# 获取单元格内容
print(a.value)

# 获取单元格所在列和行
print(‘a is ’+str((a.column,a.row)))

需要注意的是,sheet.cell(row,column)中参数分别是行和列,且必须为整数,如果列为英文字母,可以利用 openpyxl.utils 中的 column_index_from_string (char)进行字母数字的转化。顺便一说,同理也可以利用 get_column_letter(number) 进行数字字母间的转化

from openpyxl.utils import get_column_letter, column_index_from_string

# 对列进行字母/数字转化
c_num = column_index_from_string('B')  # c_num = 2
c_char = get_column_letter(5)          # c_char = 'E‘

(三)获取行和列

在处理 Excel 表格有时可能需要对表格进行遍历查找,openpyxl 中便提供了一个行和列的生成器 (sheet.rows和sheet.columns) ,这两个生成器里面是每一行(或列)的数据,每一行(或列)又由一个 tuple 包裹,借此可以很方便地完成对行和列的遍历

# 对行进行遍历,输出A1,B1,C1
for row in sheet.rows:
    for cell in row:
        print(cell.value)

# 对列进行遍历,输出A1,A2,A3
for column in sheet.columns:
    for cell in column:
        print(cell.value)

学习时还发现也可以通过 list(sheet.rows)index 对某一行或列进行遍历,而在此值得注意的是,由于sheet.rows(或sheet.columns)是生成器类型,是不能直接调用的,需将其转化为一个 list 类型,然后再通过索引遍历

# 对某一特定的行进行遍历
for cell in list(sheet.rows)[0]:
    print(cell.value)

同时,也可以通过使用 sheet[行列值:行列值] 来对给定单元格范围进行遍历

# 对某一单元格范围进行遍历
for spaces in sheet['A1':'B2']:
    for cell in spaces:
        print(cell.value)

另外,有时候我们还可能需要确定表格的大小,即获取表格行和列的最大值,可以用 max_row 和 max_column 来获取

# 获得最大列和最大行
print(sheet.max_row)
print(sheet.max_column)

写入Excel文档

在开头读取时已经介绍,默认的打开方式为可读可写,那么使用 load_workbook(filename) 读取 Excel 文档后也就可以直接写入了。另外,如果需要新建一个 Excel 文件,可以使用 Workbook()方法,同时它会自动提供一个 sheet 工作表。对于删除一个工作表,则可以使用 workbook 对象的 remove(sheet) 方法删除

# 新建一个Excel文档
wb = openpyxl.Workbook()

# 删除某个工作表 
wb.remove(sheet)

(一)写入单元格

获取工作表和之前一样,如果使用 load_workbook(filename) 读取,那么获取工作表后可以直接通过sheet[行列值]写入单元格。学习时,有资料介绍还可以传入Excel中的公式进行赋值,不过要注意,在读取文件时需要加上参数 data_only=True ,这样才能返回数字,否则将返回字符串,即公式本身

# 直接赋值
sheet['A1'].value = 2

# 公式赋值
sheet['A6'].value = '=SUM(A1:A5)'

另外,也可使用 sheet.append(parameters) 一行或多行写入

# 写入一行
row = [1 ,2, 3, 4, 5]
sheet.append(row)

# 写入多行
rows = [
    ['ID', 'Name', 'Department'],
    ['001', 'Lee','CS'],
    ['002', 'John','MA'],
    ['003', 'Amy','IS']
]
sheet.append(rows)

(二)保存文件

写完文件后,使用 workbook.save(path+filename)进行保存,不过要注意文件扩展名一定要是 xlsx 格式

# 保存文件至当前目录
wb.save('new_file.xlsx')

设置单元格样式

单元格样式主要包括字体、边框、颜色以及对齐方式等,这些均位于 openpyxl.styles 库中

# 导入字体、边框、颜色以及对齐方式相关库
from openpyxl.styles import Font, Border, Side, PatternFill, colors, Alignment

(一)字体

通过 sheet 单元格 font 属性设置字体风格

# 设置字体风格为Times New Roman,大小为16,粗体、斜体,颜色蓝色
sheet['A1'].font = Font(name='Times New Roman', size=16, bold=True, italic=True, color=colors.BLUE)

(二)对齐方式

通过 sheet 单元格 alignment 属性设置文本对齐风格

# 通过参数horizontal和vertical来设置文字在单元格里的对齐方式,此外设置值还可为left和right
sheet['B1'].alignment = Alignment(horizontal='center',vertical='center')

(三)边框

通过 sheet 单元格 border 属性设置字体风格

# 首先设置边框四个方向的线条种类
left, right, top, bottom = [Side(style='thin', color='000000')] * 4
# 再将各方向线条作为参数传入Border方法
sheet['C1'].border = Border(left=left, right=right, top=top, bottom=bottom)

(四)设置行高和列宽

行和列的长度大小可以通过 row_dimensions[序号].height 和 column_dimensions[标号].width 来设置

# 设置行高
sheet.row_dimensions[1].height = 25

# 设置列宽
sheet.column_dimensions['D'].width = 15.5

(五)合并和拆分单元格

对单元格的合并与拆分,主要是通过 sheet 的 merge_cells(args1:args2)和unmerge_cells(args1:args2) 两个方法来实现的

当然,除了对角矩形区域化合并,也可以对一行或一列进行合并,只需相应修改参数即可。不过,这里要注意的是,合并后单元格显示的文本内容是合并前最左上角单元格的内容,而其他单元格内容则会自动清除。

# 合并单元格
sheet.merge_cells('A1:B2')

# 拆分单元格
sheet.unmerge_cells('A1:B2')

python方法——现金流计算

thbcm阅读(190)

常用现金流的计算

1.固定现金流现值计算函数表达式:

PresentVal=pv(Rate,NumPeriods,Payment,ExtraPayment,Due) Rate:贴现率 NumPeriods:贴现周期 Payment 周期现金流 ExtraPayment:最后一次非周期现金流,函数默认为 0Due:现金流计息方式(0为周期末付息,1为周期初付息)PresentVal:现金流现值代码如下:
import numpy as np
Facevalue=1000
# 债券付息(面值*利率),假设每年付息一次
Payment=0.05*Facevalue
# 市场利率
Rate=0.06
# 到期还本,ExtraPayment额外现金流为本金
ExtraPayment=Facevalue
# 债券期现为10年
NumPeriods=10
# 每年年末付息
Due=0
PresentVal=abs(np.pv(Rate,NumPeriods,Payment,ExtraPayment,Due))
print(PresentVal)  #该债券价格低于926.4,则被低估,高于926.4,则被高估

结果为:926.3991294858529

2.固定现金流终值计算函数

FutureVal=fv(Rate,NumPeriods,Payment,PresentVal,Due)

Rate:贴现率

NumPeriods:贴现周期

Payment 周期现金流

Due:现金流计息方式(0为周期末付息,1为周期初付息)

PresentVal:现金流现值 # FutureVal:现金流终值

代码如下:

import numpy as np
Facevalue=1000
# 债券付息(面值*利率),假设每年付息一次
Payment=0.05*Facevalue
# 市场利率
Rate=0.06
# 到期还本,ExtraPayment额外现金流为本金
ExtraPayment=Facevalue
# 债券期现为10年
NumPeriods=10
# 每年年末付息
Due=0
FutureVal=abs(np.fv(Rate,NumPeriods,Payment,PresentVal,Due))
print(FutureVal)

结果如下:2318.079494238091

3.变化现金流计算

净现值法(NPV)将现金流用必要收益率贴现计算 NPV 值,若 NPV>0,则可行,否则不可行内部收益率法假设 NPV=0,计算必要贴现率,若 IRR 大于必要收益率则可行,反之不可行净现值 NPV 计算函数 PresentVal=npv(Rate,CashFlow)Rate:必要收益率CashFlow:现金流序列向量 PresentVal:现金流现值代码如下:

import numpy as np
# 现金流
CashFlow=[-8000,2500.1500,3000,1000,2000]
# 利率
Rate=0.08
PresentVal1=-8000+2500/1.08+1500/1.08**2+3000/1.08**3+1000/1.08**4+2000/1.08**5
PresentVal1=np.npv(Rate,CashFlow)
print(PresentVal1)

结果如下:-849.137888777871

4. 内部收益率计算函数

Return=irr(CashFlow)Return :内部收益率代码如下:

import numpy as np
CashFlow=[-8000,2500,1500,3000,1000,2000]
Return=np.round(np.irr(CashFlow),4) #round() 方法返回浮点数x的四舍五入值。如round(80.23456, 2) :  80.23
print(Return)

结果如下:0.0839

5. 年现金流计算

若投资100万买房,还款期20年,每月还6000元,则贷款利率为多少?若改为每月还8000,贷款利率不变,则还款期限为多长?年金利率函数 rate Rate=rate(NumPeriods,Payment,PresentValue,FutureValue,Due) NumPeriods:现金流周期 Payment:现金流支出 FutureValue:现金流终值,函数默认为 0Due:现金流计息方式(0为周期末付息,1为周期初付息) PresentVal:现金流现值 Rate:利息率(贴现率)

代码如下:

import numpy as np
# 贷款现值
PresentValue=1000000.0
# 每次还款金额
Payment=-6000.0
# 还款次数
NumPeriods=20*12
# 现金流终值为0
FutureValue=0
# 每周期还款一次,0为周期末付息
Due=0
# 调用rate函数
Rate=np.around(np.rate(NumPeriods,Payment,PresentValue,FutureValue),4)
print(Rate)

结果为:0.0032

继续求解,

年金周期函数 nper NumPeriods=nper(Rate,Payment,PresentValue,fv,Due)Rate:贴现率 Payment:现金流收入(支出) PresentValue:现金流现值fv:现金流终值,默认值为 0Due:现金流计息方式 NumPeriods:现金流周期

import numpy as np
# 贷款现值
PresentValue=1000000.0
# 每次还款金额
Payment=-8000.0
# 现金流终值为0
FutureValue=0
# 每周期还款一次,0为周期末付息
Due=0
# 月利率,银行的现行计息方式
Rate=0.0389/12
# 调用nper计算还款周期
NumPeriods=np.nper(Rate,Payment,PresentValue,fv=0,when='end')
print(NumPeriods/12)

结果如下:13.377524805437297

联系我们