当前位置:   article > 正文

语法:Python与C++对比_c++和python

c++和python

目录

一、概述

二、语句

三、函数

四、数据结构

五、类


一、概述

Python和C++都是面向对象的。

Python和C++的区别:

  • Python是脚本语言,脚本语言不需要编译,直接解释执行,由解释器来负责解释。程序代码即是脚本程序,也是可执行文件,只需要编写-运行。
  • C++是编译型编程语言,需要编译成二进制代码,以可执行文件形式运行,需要经过编写-编译-链接-运行的过程。             

从语法角度来说:Python更加灵活;C++逻辑更加清晰。

Python C++

import cmath

导入模块,每一个.py文件都可以认为是一个模块。Python中导入的模块可以是类的定义,函数的定义。

#include

引入头文件,头文件为类或函数的声明。

C++支持分离式编译,对源文件分别编译成目标文件,再链接生成可执行文件。通过头文件把源文件关联起来。

在python程序中直接添加sys.path,即import时寻找模块的路径

在cmakelist中设置include_directories(),即include时寻找相应头文件的路径

在程序中添加单元测试

if __name__ == "__main__":

当模块被作为脚本直接运行时,执行之后的程序块;

当模块被其他模块导入时,不执行之后的程序块。

非常方便,可以直接将单元测试脚本写在if __name__ == "__main__":后,保证程序功能正常

C++需要有一个 main 函数来作为程序的入口。如果需要单元测试需要单独写带有main函数的源文件。
数据类型决定数据所占内存空间的大小、布局方式、该空间能存储的值的范围、以及数据能参与的运算
对象

一切皆对象(数据类型、函数、类都是对象)。对象包含identity(Python中即为对象的内存地址,一旦创建不再改变)、类型信息、值、引用计数(用于记录当前有多少个变量在引用这个对象。一旦引用计数器为0,那么该对象就会被系统自动回收)、。

可参见:Python 中的对象概述 - 知乎

一块能存储数据并具有某种类型的内存空间
变量

在python中就是个name,Names refer to objects. Names are introduced by name binding operations.

没有类型信息,是通用的(增加python的灵活性),引用一个对象。

(即变量是对象内存地址的引用)

可参见:python中的变量、对象和引用_ColourfulPanda的博客-CSDN博客_python 引用对象

命了名的对象,即具名的、存储数据并具有某种类型的内存空间

(即变量有一个名字,并在内存中占据一定的存储单元,在该存储单元中存放变量的值)

引用引用即别名,为已经存在的对象所起的另外一个名字,引用自身不是对象
指针指针用来存放某个对象的地址,指针自身也是一个对象
=

name binding,名字绑定,变量存储对象的引用

对象分为可变对象和不可变对象,进行name binding时内存空间示意图可参见:Python中的变量、对象 - 汉尼拔草 - 博客园

把值赋给变量(对象)

二、基本操作

Python

C++

int最大整型\最小整型

O

备注:sys.maxsize \ -sys.maxsize-1 (跟计算机相关,可能是32位,也可能是64位)

sys.maxint \ -sys.maxint-1在python2中支持,在python3中不支持了

INT_MAX  \  (4Bytes,即32bits的最大值2^31-1)

INT_MIN  (-2^31)

float最大值\最小值

float('inf')  \

float('-inf')

FLT_MAX  \

FLT_MIN

两字符相减字符不能直接相减,必须先用ord()函数返回字符串的ASCII值,才能相见字符可以直接相减
/两个整型做“/”运算,得到浮点型的结果两个整型做“/”运算,得到整型,相当于除完取int(),小数部分截断,即向0取整

// 地板除,即向负无穷取整

8//3 结果为2

-8//3 结果为-3

%

% 取模(modulus)

-10%3 结果为2

-90%8结果为6

% 取余

-10%3 结果为-1

-90%8结果为-2

PS:

取模(Modulus ) V.S. 取余(Remainder)

对于整型数a,b来说,取模运算或者求余运算的过程都是:

1. 求 整数商: c = a/b;  (把这里的/理解成数学意义的除。求模运算和求余运算在这一步不同:取模运算在本步骤计算c的值时,向负无穷方向舍入(向下取整);取余运算在本步骤取c的值时,向0 方向截断;

2. 计算模或者余数: r = a - c*b (本步骤取模和取余相同)

以python实现为例,取模和取余代码区别为:

  1. # 取模,Python中可直接用%,计算模,r = a % b
  2. def mod(a, b):
  3. c = a // b
  4. r = a - c * b
  5. return r
  6. # 取余
  7. def rem(a, b):
  8. c = int(a / b)
  9. r = a - c * b
  10. return r

Python中的取模运算和取余运算 - Tiffany,fu - 博客园

符号%在C++和Python中意义不同

对于%符号,在上面第1步中:

C++使用的a/b (是0取整),所以最终C++中%实现的是取余;

python使用的是a//b(是向负无穷大取整),所以python中%实现的是取模。

三、控制流

PythonC++
if

if condition1:
    statement1
elif condition2:
    statement2
else:
    statement

在if , elif, else中只要匹配上一个,后面的条件和代码块都不再执行,即便后面的elif条件也能匹配上。

if (condition1){
    statement1;

}
else if (condition2){
    statement2;

}
else (condition3){
    statement3;

}

三元运算符target = a if condition else btarget= condition?a:b;
for循环

for item in iterable:
    statement

iterable的可以是列表Occupationt=['teacher','student','driver','governer']、元组(1,2,3)、字典{"name":'Arnold',"Salary":10000}.items()、集合对象{100,‘ab’}、字符串 ‘abcdefg’。

for i in range(0,10):
    print(i)

for (init-state;condition;expression){
    statement;

}

C++范围for语句实现类似于Python的遍历序列:

for (auto item : iterable){
    statement;

}

iterable为一个序列,可以是数组、vector、string, 必须拥有能返回迭代器begin和end的成员。 

for (i=0;i<10;i++){
    print(i);

}

while循环while condition:
    statement

while (condition){
    statement;

}

switchO

switch(condition){

        case(constant-expression1):

                statement1;

        case(constant-expression2):

                statement2;

                break;

        case(constant-expression3):

                statement3;

                break;

        default:

                statement3;

}

判断满足条件的case后,后面的语句依次执行(不管后面的case是不是能匹配上),直到遇到break。在上面例子中,如果匹配了constant-expression1,则statement1和statement2都会执行。

总结:

1. Condition书写格式:

  • 在if, for,while后面的条件,Python直接写;
  • C++需要把条件写在括号()内。

2. 代码块:

  • Python使用冒号:指出接下来是一个代码块,并将代码块中的每行都缩进相同的空格(悬垂的格式);
  • C++ 用{}表示代码块;

3. 作用域:

  • Python中if, for, while的代码块不单独创建局部作用域,即在其中创建的变量在该代码块之外也可以使用;
  • C++在if, for, while后的代码块中创建局部作用域,在其中创建的变量在该代码块之外不可见

四、函数

PythonC++
函数传参

传对象引用(pass by object reference): 传递的是对象的内存地址的引用;

实质与python的=意义相同。

传值(pass by value):把实参的实际值赋值给函数的形参。在函数内修改形参,函数外的实参不改变。

传引用(pass by refrence):把实参的引用赋值给形参。在函数内修改形参,函数外的实参随之变化。

函数内定义函数

(嵌套函数)

python的函数也是对象,因此可以将函数作为参数传递、可以将函数作为返回、可以将函数放入结构体中(如函数放入数组中)、可以在函数中定义函数;

def outer():

    x=5

   def inner():

         y=x+3   #内函数在自己作用域内查找局部变量失败后,会进一步向上一层作用域里查找。

         print(y)

    return inner  #闭包:内函数体内引用到外函数中定义的变量,return返回内函数的引用时把内函数和外部引用变量打包成整体返回, 这里inner函数和外部变量x组成闭包

不能在函数中定义函数,需要使用函数对象或者匿名函数实现需求

void foo(){

    //function object

    class InnerFunction{

        public:

            void operator()(){

                 std::cout<<"inner function object"<

            }

    };

    InnerFunction innerfunction;

    innerfunction();

    //lambda expression

    auto lambdafunc=[=]()->void{

        std::cout<<"inner lambda function"<

    }

    lambdafunc();

}

装饰器

用于扩展函数功能:

  1. def executetime(func):
  2.    def wrapper(*args, **kwargs):
  3.         time1=time.time()
  4.         func(*args, **kwargs)
  5.         time2=time.time()
  6.         delta_t=time2-time1
  7.         print('excecute_time is {}'.format(delta_t))
  8.    return wrapper
  9. @executetime
  10. def myfunc(num):
  11.     time.sleep(num)
  12. #调用时:
  13. myfunc(5)
O
生成器

生成器是包含yield语句的函数,是使用普通函数语法定义的迭代器。

当生成器被调用时不会执行函数体内的代码,而是返回一个迭代器。

每次使用next()请求值时都执行生成器的代码,直到遇到yield或return,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,并返回 yield 的值。在下一次执行 next() 方法时从当前位置继续运行。

O

装饰器

想扩展类的功能可以使用继承(新增方法或者重写方法),那么想扩展函数的功能怎么办?把函数到处复制是非常丑陋的,Python的解决方案是“装饰器”。装饰器用于扩展函数功能,在没对函数本身做任何修改的情况下,如其名所示,添加了其它(辅助)功能。

其依赖于Python中一切皆对象的原理,可以在函数中定义函数、可以把函数作为另一个函数的argument输入、可以在函数中return函数。

装饰器概念的引入:

假设有一个函数:

  1. def myfunc(num):
  2.     time.sleep(num)

假设想给函数加上执行时间的计时,最直接的方法是直接改写函数:

  1. def myfunc(num):
  2.     time1 = time.time()
  3.     time.sleep(num)
  4.     time2 = time.time()
  5.     delta_t=time2-time1
  6.     print('excecute_time is {}'.format(delta_t))
  7. #调用时:
  8. myfunc(5)

这里出现两个问题:1. 直接改写别人的函数在很多情况下是不允许的,而且会使得原函数引入很多冗余功能;2. 如果想要添加的这个功能也是具有通用性的,对很多函数都要添加计时,难道要把重复的代码写进每一个函数?

直观的方法是重新定义一个函数,专门实现计时功能,在该函数内部调用原来的函数(这里使用了python可以把函数作为另一个函数的argument输入的原理):

  1. def executetime(func, *args):
  2.     time1 = time.time()
  3.     fun(*args)
  4.     time2 = time.time()
  5.     delta_t=time2-time1
  6.     print('excecute_time is {}'.format(delta_t))
  7. def myfunc(num):
  8.     time.sleep(num)
  9. #调用时:
  10. excecutetime(myfunc, 5)

这里独立定义了计时功能的函数,保证了计时这个功能代码的复用性,但是又出现了一个问题:代码执行的逻辑发生改变了,原来是想执行myfun(5),现在变成了执行excecutetime(myfunc, 5) ; 为了解决这个问题,可以加一层wrapper函数封装(这里还使用了可以在函数中定义函数,以及在函数中return函数的原理):

  1. def executetime(func):
  2.    def wrapper(*args, **kwargs):
  3.         time1=time.time()
  4.         func(*args, **kwargs)
  5.         time2=time.time()
  6.         delta_t=time2-time1
  7.         print('excecute_time is {}'.format(delta_t))
  8.    return wrapper
  9. myfunc = executetime(myfunc)
  10. #调用:
  11. myfunc(5)

装饰器其实是一个语法糖,是对myfunc = executetime(myfunc)这种写法的一种简写,即直接在定义函数是加上:

  1. @executetime
  2. def myfunc(num):
  3.     time.sleep(num)

所以,含有装饰器的完整定义如下:

  1. def executetime(func):
  2.    def wrapper(*args, **kwargs):
  3.         time1=time.time()
  4.         func(*args, **kwargs)
  5.         time2=time.time()
  6.         delta_t=time2-time1
  7.         print('excecute_time is {}'.format(delta_t))
  8.    return wrapper
  9. @executetime
  10. def myfunc(num):
  11.     time.sleep(num)
  12. #调用时:
  13. myfunc(5)

这样就保证了在原来调用形式不变的情况下扩展这可以计时这种共用的功能,而且这个装饰器的代码可以复用,轻松地给其他函数扩展计时的功能。

这里可以看到装饰器的函数定义时外面裹了两层,最外面一层def execute(func): return wrapper的目的就是把被装饰的函数送进去;里面一层def wrapper(*args, **kwargs)则是把被装饰的函数的参数送进去。那么如果装饰的函数也需要传参数怎么办呐?答案是在外面再裹一层,使用时注意在@后直接调用最外面裹的函数并传参。

  1. def executetimelogger(logfile='out.log'):
  2. def executetime(func):
  3. def wrapper(*args, **kargs):
  4. time1=time.time()
  5. func(*args, **kwargs)
  6. time2=time.time()
  7. delta_t=time2-time1
  8. print('excecute_time is {}'.formate(delta_t))
  9. with open(logfile,'a') as opened_file:
  10. opened_file.write(str(delta_t)+'\n')
  11. return wrapper
  12. return executetime
  13. @executetimelogger(logfile='myout.log')
  14. def myfunc(num):
  15. time.sleep(num)
  16. #调用时:
  17. myfunc(5)

 参考:python 闭包和装饰器详解_探索未知的自己的博客-CSDN博客_python 闭包

其他常用装饰器:

  • @property,即保证类的封装特性(不能访问私有成员),又使得开发者可以通过“对象.属性”的方法操作类属性;

参考:Python @property装饰器详解 - 知乎

  • @staticmethod,在不创建类的实例的条件下调用被装饰的方法,其参数列表不需要使用self,静态方法不能引用类中的属性或方法。(很多应用场景是类对外部函数的封装);
  • @classmethod,需要第一个参数传入cls,可以修改类的属性,不能对实例的属性进行操作,常用来实现工厂方法,比如实现初始化函数的动态重载功能;

参考:classmethod() in Python - GeeksforGeeks

五、数据结构

Python和C++都有容器的概念:

序列中的每个元素被分配一个序号--即元素的位置,也称为索引。

通用序列操作:索引、分片、序列相加、乘法、成员资格、长度、最小值和最大值。

Python包含6种内建的序列,即列表、元组、字符串、Unicode字符串、buffer对象和 xrange 对象

  • 列表 []: 相当于C++中的vector容器;数据项不需要具有相同的类型
  • 元组 (): 元组的元素不能修改;数据项不需要具有相同的类型

Python常用的基本内置数据类型还包括:

  • 集合{ } 或 set() : 无序不重复序列
  • 字典 {}   
  • Python中在每一个容器的方法中定义了大量的操作,例如列表的方法直接实现查询、修改等操作,优点是方便、直观;
  • C++在每个容器的方法中只定义了添加、删除等很少的操作,而是以泛型算法的形式实现更多的查找元素、替换或删除特定值、排序等操作,优点是可以用于多种容器类型。泛型算法算法不直接操作容器,通过配合迭代器使用,遍历由两个迭代器指定的一个元素范围来进行操作,使得算法不依赖于容器类型。

1. 顺容器操作

Python 

序列(sequence ,包括列表、字符串和元组)

C++

顺序容器(sequential container,包括vector, deque, list, forward_list, , string, array)

创建

例如:v=['dlsdkf', 60]

无需声明容器的类型以及容器内元素的类型:使用[]表示创建列表,()表示元组,“”或''表示字符串;

列表中可以放任意类型的元素,甚至在同一列表中可以包含不同类型的元素。

C c{a,b};

例如: std::vector v{2,3,5};

列表初始化

需要声明容器的类型和容器内元素的类型,容器内元素类型必须相同。

索引

v[n], n可为负数,-1表示倒数第一个

v[n], n只能为正数

切片

v[n:n+m]   (左闭右开)

v[n:n+m:k] 支持带步长的切片

O
序列相加

v1+v2将两个序列拼接起来

O

v*5,重复序列v 5次 (乘法)

O
成员资格

60 in v

O
len(v)v.size();
min(v)O
max(v)O

a. 列表

Python 列表的方法

C++

顺序容器(sequential container,包括vector, deque, list, forward_list, array, string)

创建列表

支持列表推导,通过列表及简洁语法生成新的列表。例如,创建二维列表:

dp=[[0]*dim2 for _ in range(dim1)]

dp=[[0 for _ in range(dim2)] for _ in range(dim1)]

三维列表:

dp=[[[0 for _ in range(dim3)] for _ in range(dim2)] for _ in range(dim1)]

创建二维列表:

vector<vector<int>> v(dim1, vector<int>(dim2,0);

创建三维列表:

vector<vector<vector<int>>> v(dim1, vector<vector<int>>(dim2, vector<int>(dim3, 0)));

计算指定元素出现次数

v.count(60)

列表中查找第一次出现的索引

v.index(60)

O (可通过泛型算法find实现)
复制列表

v2=v1.copy()

反序排列

v.reverse()

O
排序

v.sort()

O(可通过泛型算法sort实现)
末尾添加一个对象

v.append(20)

v.push_back()
末尾添加多个对象

v1.extend(v2)

v.insert()
将一个对象插入列表

v.insert(2, 69)

v.insert()
删除元素

del v[1]

v.erase()
删除开头或末尾元素

v.pop()

v.pop(0)

v.pop_back()

v.pop_front()

删除第一个为指定值的元素

v.remove(60)

O
清空列表

v.clear()

v.clear()

备注:O表示无直接命令,需要通过其他方法才能实现。

列表的深拷贝、浅拷贝及对应的内存图可参见:Python基础知识(六)—关于列表深拷贝和浅拷贝及内存图 - maplethefox - 博客园

b. 字符串

Python

C++

子串

s[2:6]

s.sub(2,8);

搜索子串

s.find(sb) 返回子串首次出现的位置,若查找不到返回-1

s.index(sb) 返回子串首次出现的位置,若查找不到抛出异常

string::size_type res=s.find(sb);

如果找到则返回首次出现的位置,否则res==s.npos

2. 关联容器

a. 字典

PythonC++
成员资格

d=dict()

if 5 in d:

        statements

unordered_map<int, int> d;

if(d.find(5)!=d.end()){

        statements;

}

六、类

PythonC++
类的构造函数和析构函数

class Foobar:

    def __init__(self):

        

    def __del__(self):

使用魔法(特殊)方法机制,魔法方法是以__开头和__结尾的方法,在特定情况下自动被Python调用,而无需手动直接调用

class Foobar{

    Foobar(){};

    ~Foobar(){};

};

和类同名的函数,无返回类型,

方法和数据成员绑定到对象

self

类定义中成员函数的第一个参数必须显式为self

对象调用成员函数时不用传入该参数,python解释器会自己把实例变量传进去。

this  常量指针,指向对象地址

类的成员函数通过this的隐式形参访问调用它的对象。类中定义成员函数时和对象调用成员函数时都不用显式传入this。

在成员函数中return *this返回的正是调用该函数的对象

数据成员

python中称为对象的数据成员

在类的构造函数内直接定义:

class Example:

    def __init__(self):

        self.mymember=1

也可以在对象中直接添加或删除对象的数据成员:

example=Example()

example.mysecondmember=2.

del example.mymember

这充分体现了Python的灵活性。

在类的方法外声明数据成员,随后对类实例化,每一个对象拥有数据成员;在对象中无法再添加或删除数据成员

class Example{

    Example(int x):member(x){};

    int mymember;

};

Example example;

C++的数据成员一目了然,非常清晰。

类的静态成员

python中称为类的数据成员

class Example:

    classmember=4

在类中但不在类的方法中定义的数据成员为类的数据成员,所有类的实例化对象共享类的数据成员,在类内或类外可以通过Example.classmember获取类的数据成员

class Example{

    static init mymember;

};

int x;

x=Example::mymember;

类方法

class Example:

    classmember=4

    @classmethod

    def getclassmember(cls):

        return cls.classmember

静态成员函数

(类的静态成员函数不与任何对象绑定在一起,不包含this指针)

class Example:

    @staticmethod

    def  initsample():

           pass

直接调用:Example.initsample()

也可实例化后调用:

example=Example()

example.initsample()

class Example{

    static double initsample();

};

直接调用:

double r;

r=Example::initsample();

也可实例化后调用:

Example example;

example.initsample();

子类的构造函数初始化父类

class Fathertarget:

    def __init__(self, Parameter1,Parameter2):

class Target( Fathertarget):

    def __init__(self, P1, P2):

          super().__init__(P1 =Parameter1,P2=Parameter2)

class Fathertarget{

Fathertarget(int Parameter1, int Parameter2){};}

class Target:public Fathertarget{

Target(int P1, int P2):Fathertarget{P1,P2}{}

}

在子类的构造函数初始化列表中调用父类的构造函数初始化

抽象基类

(作为接口)

class Example:

    @abstractmethod

    def  examplemethod(self):

        pass

class Example{

    void examplemethod()=0;

};

像调用函数一样调用类实例对象

class Sample:

    def __call__(self):

        print("!!!!!")

sample=Sample()  #实例化

sample()

使用函数对象,在类中重载()运算符
多态

对象遵守特定的协议,协议指定需要实现哪些方法以及这些方法应做什么。不基于类的继承关系。

基类的指针或引用可以绑定在继承类的对象上,基于此机制实现多态。
迭代器实现了方法__iter__的对象是可迭代的,实现了方法__next__的对象是迭代器O


 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/500465
推荐阅读
相关标签
  

闽ICP备14008679号