Common Python and C++11 usage comparison
Contents
Define Variables
- Python
a = 1
b = 0.0
c = 'xxx'- C++
int a = 1;
auto a = 1;
double b = 0.0;
auto b = 0.0;
const char c[] = "xxx"; // C string (char array)
const char* c = "xxx"; // C string (pointer)
std::string c = "xxx";
std::string c("xxx");
std::string c{"xxx"};Variable Scopes
- Python
global_var1 = 1
def func(arg):
local_var = 2
global global_var2
global_var2 = 'xxx' # global
if arg:
local_var2 = 0.5 # scope: function
...
# if arg evaluates to True,
# local_var2 is still accessible here.
return False- C++
int global_var1 = 1; // global
static std::string global_var2; // global in the current file
bool func(bool arg){
int local_var = 2;
global_var2 = "xxx";
if(arg){
double local_var2 = 0.5; // scope: if block
....}
// local_var2 is undefined here}
Reference
- Python
s1 = {"key1" : 100}
s2 = s1 # reference the same object
s2["key2"] = 200
print(s1["key1"])
# {"key1":100, "key2": 200}
s2 = {} # s1 is NOT changed, s1 and s2
# reference different objects now- C++
std::unordered_map<std::string, int> s1 = {"key1" : 100};
std::unordered_map<std::string, int> s2 = s1; // copy the whole object
std::unordered_map<std::string, int>& s2 = s1; // reference the same object
s2 = std::unordered_map<std::string, int>(); // s1 is changed, s1 and s2 still reference the same object
// C++ 11
auto s2 = s1; // copy the whole object (slow)
auto& s2 = s1; // reference the same object
// Now s2 is a reference to s1
auto s3 = s2; // copy the whole s1 object. s3 is NOT a reference
const auto& s3 = s2; // reference the same s1 object
Conditional
- Python
if a == 1 and b == 2:
pass
elif c == 3 or d == 4:
pass
else:
pass
if a == 1:
...
elif a == 2:
...
elif a == 3:
...
else:
...- C++
if (a == 1 && b == 2){}
else if(c == 3 || d == 4){}
else {}
switch(a){
case 1:
...
break; // without break, will run case 2 as well
case 2: { // create a new scope if we need to define new variables
int a = 100;
break;}
default: // it's good practice to always add this
...};Namespace & Imports
- Python
# Import: File: app/bidders/ai_util.py
import app.bidders.ai_util
# Namespace: defined by directory structure
# Fully qualified names:
app.bidders.ai_util.func("xxx")- C++
// Import: Files: app/bidders/ai_util.hpp & ai_util.cpp
#include "app/bidders/ai_utils.hpp"
// Namespace: not related to directory structure
namespace app {
namespace bidder {
namespace ai_util {
void func(const char* str);
}
}
}
// Fully qualified names:
app::bidders::ai_util::func("xxx");Loops
- Python
for i in range(100):
pass
while cond:
...
a = [0, 1, 100]
for item in a:
pass
b = {"key": 0.5, "key2", 1.0}
for key, val in b.iteritems():
pass- C++
for(int i = 0; i < 100; ++i){...}
while(cond){}
std::vector<int> a = {0, 1, 100};
for(auto& item: a){ // without &, this will copy each item
}
std::unordered_map<std::string, double> b = {{"key", 0.5}, {"key2",
1.0}};
for(auto& item: b){ // without &, this will copy each item
auto& key = item.first;
auto& val = item.second;}Functions
- Python
def func(arg1, arg2):
....
return ret1, ret2, ret3- C++
void func(const Arg1& arg1, const Arg2& arg2,
Ret1& ret1, Ret2& ret2, Ret3& ret3
){...
ret1 = ....;
ret2 = ....;
ret3 = ....;}
No multiple return values
Need to specify the type of the return value
Every variable needs to have type declaration
Declaration before use is required
Add const to the references that are not changed by the method
Declare in *.hpp, implement in *.cpp (for public functions)Class Definition
- Python
# Python version: only one *.py file:
class PythonClass(ParentClass):
def __init__(self): # constructor, pass instance as self
ParentClass.__init__(self) # python2
self.attrib = 1111
Self.attrib2 = 'xxx'
def some_method(self, arg1, arg2):
return arg1 * arg2 + self.attrib
def _some_private_method(self): # kind of private func
pass- C++
// --------- Declaration: cpp_class.hpp ---------
class CppClass: public ParentClass {
public:
CppClass(): ParentClass(), attrib(5566),
attrib2("xxx"){}
virtual ~CppClass(): {
// destructor: free allocated resources here}
double someMethod(double arg1, double arg2);
private:
void somePrivateMethod(){}
int attrib;
std::string attrib2;};
// --------- Implementation: cpp_class.cpp ---------
#include "cpp_class.hpp"
double CppClass::someMethod(double arg1, double arg2){
return arg1 * arg2 + attrib;}Virtual function
- Python
class Raccoon:
def get_name(self):
return "raccoon"
class Zebra(Raccoon):
def get_name(self):
return "zebra_" + Raccoon.get_name()
def func(maybe_racoon):
print(maybe_racoon.get_name())
obj = Zebra()
func(obj)
# pintout: zebra_raccoon- C++
class Raccoon {
public:
std::string getName() const {
return "raccoon";}};
class Zebra: public Raccoon {
public:
std::string getName() const {
return "zebra" + Raccoon::getName();}};
void func(const Raccoon& maybeRaccoon){
std::cout << maybeRaccoon.getName() << std::endl;}
Zebra obj;
func(obj);
// pintout: raccoon
- C++
class Raccoon {
public:
virtual std::string getName() const {
return "raccoon";}};
class Zebra: public Raccoon {
public:
std::string getName() override const {
return "zebra" + Raccoon::getName();}};
void func(const Raccoon& maybeRaccoon){
std::cout << maybeRaccoon.getName() << std::endl;}
Zebra obj;
func(obj);
// pintout: zebra_raccoon
Manage Objects
- Python
obj = ObjClass()
obj.method(arg)
obj.attribute = 100
obj2 = obj # reference the same object
# Manual delete is not needed- C++
ObjClass* obj = nullptr; // prefer nullptr over NULL
ObjClass* obj = new ObjClass(); // allocate on heap
obj->method(arg);
obj->attribuge = 100;
auto obj2 = obj; // point to the same object
auto obj2 = *obj; // copy!
auto& obj2 = *obj; // reference the same object
delete obj; // when not used, manual delete is required
ObjectClass localObj(); // allocate on local stack
localObj.method(arg);
// Raw pointer is not recommended. Use smart pointers
#include <memory>
std::shared_ptr<ObjClass> obj;
auto obj = std::make_shared<ObjClass>();
obj->method(arg);
obj->attribute = 100; // manual delete is not needed
auto obj2 = obj; // point to the same object (no * or &)
Common Data Types (from Python to C++)
int:- int, long, unsigned int, unsigned long (size is architecture dependent)
- std::int64_t, std::uint64_t, std::int16_t, … (#include
, well-defined sizes)
bool:boolfloat: double (64-bit), float(32-bit, bad performance & not recommended)str, bytes: std::string (#include) - containers:
list: std::vector<> (#include) dict: std::unordered_map<> (#include <unordered_map>)set: std::unordered_set<> (#include <unordered_set>)
- None:
- For float, can use NAN (#include
) and use std::isnan(number) to check if it’s NAN - For string, just use empty string and use str.empty() to check if it’s empty
- For float, can use NAN (#include
Define Strings
- Python
s = "this is a string"
s2 = s # s2 and s reference the same object
len(s)
t = "prefix_" + s + "_suffix"
t = "prefix1" + "prefix2" + s
s = "has\0zero"
# len(s): 8- C++
#include <string>
std::string s = "this is a string";
auto s2 = s; // copy s to s2 (new object)
auto& s2 = s; // s2 is a reference only
s.length();
auto t = "prefix_" + s + "_suffix"; // works but slower
std::string t = "prefix_"; t += s; t += "_suffix"; // good
auto t = "prefix1" + "prefix2" + s; // does not work
std::string s = "has\0zero"; // incorrect
s.length(): 3
std::string s("has\0zero", 8); // correct
Alternative (C++ 14):
using namespace std::string_literals;
auto z = "has\0zero"s; // add "s" suffix, z is std::string
auto z = "has\0zero"; // z is char* pointer
String Methods
- Python
t = "test str"
if t.find("sub_str") == -1:
print("not found")
u = t[1:2] # get sub string
u = t[2:] # get sub string til end
if not t:
print("empty str")
v = t.lower()- C++
#include <string>
std::string t = "test str";
if (t.find("sub_str") == std::string::npos)
std::cout << "not found\n";
if(t.empty())
std::cout << "empty str\n";
auto u = t.substr(1, 2);
auto u = t.substr(1);
#include <algorithm>
#include <cctype>
std::transform(t.begin(), t.end(), t.begin(), std::tolower);
// This does not work in unicode
List (dynamic array)
- Python
a = [1, 2, 3]
b = ["str1", "str2", "str3"]
c = ["xxx", {}, 100, 0.5] # cannot be done in C++
a.append(100)
a.insert(2, 10)
del a[1], a[0:2]
tmp = a[0:2]
tmp = a[2]
tmp2 = b[1] # reference the element- C++
#include <vector>
std::vector<int> a = {1, 2, 3};
std::vector<std::string> b = {"str1", "str2", "str3"};
std::vector<????> c // cannot be done in C++
a.push_back(100);
a.insert(a.begin() + 2, 10);
a.erase(a.begin() + 1);
a.erase(a.begin(), a.begin() + 2);
std::vector<int> tmp{a.begin(), a.begin() + 2};
auto tmp = a[2];
auto tmp2 = b[1]; // copy the element!
auto& tmp2 = b[1]; // reference the element
Set
- Python
a = set()
a = {"1", "2", "3"}
b = [1, 2, 3]
c = set(b)
a.add("x")
a.remove("2")
if "4" in a:
pass- C++
#include <unordered_set>
std::unordered_set<std::string> a;
std::unordered_set<std::string> a = {"1", "2", "3"};
std::vector<int> b = {1, 2, 3};
std::unordered_set<int> c(b.begin(), b.end());
a.insert("x");
a.erase("2");
if (a.find("4") != a.end()){
...}Dict
- Python
d = {"a": 1, "b": 2}
nested = {
"a": {"a1": 0.5},
"b": {"b1": 0.3, "b2": 0.4},}
free = {"a": 100, "b": "xxx", 50: None} # cannot do this in C++
d = defaultdict(lambda: "null"); # cannot easily do this in C++ - C++
#include <unordered_map>
std::unordered_map<std::string, int> d = {
{"a", 1}, {"b", 2}};
std::unordered_map<std::string,
std::unordered_map<std::string, int>> nested = {
{"a": {{"a1", 0.5}}},
{"b": {{"b1", 0.3}, {"b2", 0.4}},};Common Dict Operations
- Python
d["new_key"] = 100
d["no such key"] # raise KeyError
del d["key"]; gc.collect()
if "key" in d:
e = d["key"]
for key, val in d.iteritems():
pass- C++
d["new_key"] = 100;
d["no such key"] // create a new item for it
d.erase("key");
auto iter = d.find("key");
if(iter != d.end()){
// without &, this will do copy
auto& e = iter->second;}
// C++ 11 ranged for loop syntax
for(auto& item: d){ // without &, this will do copy
auto& key = item.first;
auto& val = item.second;
...}