美文网首页
C/C++/Python基本知识总结

C/C++/Python基本知识总结

作者: ShowMeCoding | 来源:发表于2023-04-04 17:36 被阅读0次

    const关键字

    • const的作用:被const修饰的值不能改变,是只读变量,必须在定义的时候赋初值
    • 修饰指针分类:常量指针,指针常量
    #include <iostream>
    int main() {
        //1-常量指针
        int tmp1 = 10;
        int tmp2 = 11;
        const int* a = &tmp1;  //const修饰指针
        //*a = 9; //错误:更改对象值
        tmp1 = 9; //正确:在原对象上修改
        a = &tmp2; //正确:指针的指向可以修改,指向别的对象
    
        //2-指针常量
        int tmp3 = 12;
        int tmp4 = 14;
        int* const p = &tmp3;  //const修饰指针指向的值
        *p = 9;        //正确,可以修改指针指向的对象的值
        //p = &tmp4;   //错误:不可以修改指针指向的对象
        return -1;
    }
    

    C/C++中内存泄漏以及解决方法?

    • 内存泄漏:已动态分配的堆内存由于某种原因导致程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢,甚至系统崩溃
    • 解决方法:使用new或malloc动态分配堆上的内存空间,而没有使用delete或free及时释放内存

    C/C++中野指针的概念?

    • 野指针:也叫空悬指针,不是指向NULL的指针,是未初始化或者未清零的指针
    • 产生原因:指针变量未及时初始化;指针free或delete之后没有及时置空
    • 解决方法:定义指针变量及时初始化或者置空;释放操作之后立即置空

    C/C++中面向对象和面向过程的区别?

    • 面向对象(Object Oriented Programming,OOP):编程模型首先抽象出各种对象(各种类),并专注于对象与对象之间的交互,对象涉及的方法和属性都封装在对象内部
    • 面向过程(Procedure Oriented Programming,POP):将问题分解成若干个步骤(动作),每个步骤(动作)使用一个函数来实现,使用的时候将数据传递给这些函数
    • 二者的区别:
    • 1-安全性角度:面向对象安全性更高,面向对象将数据访问隐藏在类的成员函数中,而且类的成员变量和成员函数都有不同的访问属性,面向过程没有方法来隐藏程序数据
    • 2-程序设计角度:面向过程通常将程序分为一个个的函数;面向对象通常使用一个个对象,函数通常是对象的一个方法
    • 3-逻辑过程角度:面向过程采用自上而下的方法;面向对象通常采用自下而上的方法
    • 4-程序扩展性角度:面向对象编程更容易修改程序,更容易添加新功能

    C/C++中常用容器功能汇总

    • vector(数组):vector是封装动态数组的顺序容器
    • queue(队列):容器适配器,他是FIFO(先进先出)的数据结构。
    • deque(双端队列):有下标顺序容器,它允许在其首尾两段快速插入和删除。
    • set(集合):集合基于红黑树实现,有自动排序的功能,并且不能存放重复的元素。
    • unordered_set(无序集合):无序集合基于哈希表实现,不能存放重复的元素。元素类型必须可以比较是否相等,因为这可以确定元素什么时候相等。
    • unordered_map(无序哈希表):关联容器,含有带唯一键的键-值对。搜索、插入和元素移除拥有平均常数时间复杂度。元素在内部不以任何特定顺序排序,而是组织进桶中。元素放进哪个桶完全依赖于其键的哈希。这允许对单独元素的快速访问,因为一旦计算哈希,则它准确指代元素所放进的桶。

    C/C++中指针和引用的区别

    • C语言指针具有直接操控内存的能力,C++在指针基础上新增了引用
    • C++中对象的定义:对象是指一块能存储数据并具有某种类型的内存空间
    • 一个对象a,它有值和地址&a,运行程序时,计算机为该对象分配存储空间,来存储该对象的值,我们通过该对象的地址来访问存储空间中的值
    • 指针p也是对象,它同样有地址&p和存储的值p,只不过p存储的是其他对象的地址。如果要以p中的存储数据为地址访问对象的值,需要解引用操作符,即p
    • 对象有常量(const)和变量之分,指针常量(这个对象存放的地址是不可改变的),常量指针(指向常量的指针)
    • 引用:可以理解为给变量起别名,定义一个引用时,程序应该把该引用和它的初始值绑定在一起,而不是拷贝它。计算机必须在声明引用r的同时将它初始化,并且r一旦声明,就不可以再和其他对象绑定在一起
    • 指针和引用的区别:
    • 1.1-指针是一个新的变量,要占用存储空间,存储了另一个变量的地址,可以通过访问这个地址来修改另一个变量。
    • 1.2-引用只是一个别名,还是变量本身,不占用具体存储空间,只有声明没有定义,对引用的任何操作就是对变量本身进行操作,以达到修改变量的目的
    • 2.1-引用只有一级,指针可以多级
    • 3.1-指针传参的时候,还是值传递,指针本身的值不可以修改,需要解引用才能对指向的对象进行操作。引用传参的时候,传进来的就是变量本身,因此变量可以被修改
    • 4.1-引用一定不为空,因此相对于指针,它不用检查它所指对象是否为空,这样就提高了效率
    • 5.1-引用必须初始化,指针可以不初始化
      “&”不仅能表示引用,还可以表示成地址,还有可以作为按位与运算符。这个要根据具体情况而定。比如上面的例子,等号左边的,被解释为引用,右边的被解释成取地址。
      引用的操作加了比指针更多的限制条件,保证了整体代码的安全性和便捷性。引用的合理使用可以一定程度避免“指针满天飞”的情况,可以一定程度上提升程序鲁棒性。并且指针与引用底层实现都是一样的,不用担心两者的性能差距。
    int a, b, *p, &r = a;   //正确
    r = 3; //正确,等价于 a = 3
    int &rr; //出错,引用必须初始化
    p = &a;  //正确,p中存储a的地址,即p指向a
    *p = 4;  //正确:p中存储a的地址,a对应的存储空间存入值 4
    p = &b;  //正确:p可以多次赋值,p存储b的地址
    

    C/C++中宏定义的相关知识

    • 宏定义:把一个名称指定成任何一个文本,在完成宏定义之后,无论宏名称出现在源代码的何处,预处理器都会将其替换成指定的文本
    • 宏定义优点:
    • 1-方便程序修改,如果一个变量在程序中大量使用,可以使用宏定义为其设置一个标识符,当我们想修改这个常量时,直接修改宏定义就可以,不必在海量的代码中查找所有相关位置
    • 2-提高程序运行效率:使用带参数的宏定义可以完成函数的功能,但同时又比函数节省系统开销,提升程序运行效率(无需调用函数这个流程)
    • 宏定义和函数的区别:
    • 1-宏在预处理阶段完成,之后替换的文本参与编译,相当于恒等代换过程
    • 2-宏定义没有返回值;函数调用有返回值
    • 3-宏定义参数没有类型,不进行类型检查;函数参数具有类型,需要检查类型
    • 4-宏定义不是说明或者语句,结尾不用加分号
    • 5-宏定义必须写在函数之外,作用域为宏定义命令开始到源程序结束,如果要终止其作用域,可以使用 # undef 命令;而函数作用域在函数调用处
    //define 宏名
    #define WeThinkIn 6666589
    //define 宏名(参数) 文本
    #define R(a, b) (a/b)
    

    C/C++中typedef关键字的相关知识

    • 使用typedef关键字来定义自己习惯的数据类型名称,来代替系统默认的基本类型名称以及其他类型等名称
    • typedef 和 #define 的区别
    • 1-宏主要用于定义常量及书写复杂的内容;typedef主要用于定义类型别名
    • 2-宏替换发生在预处理阶段,属于文本恒等替换;typedef是编译中发挥作用
    • 3-宏定义参数没有类型,不进行类型检查;typedef参数具有类型,需要检查类型
    • 4-宏不是语句,不用在最后加分号;typedef是语句,要加分号标识结束
    • 5-注意对指针的操作,typedef char * p_char 和 #define p_char char * 区别巨大
    //1-为基本数据类型定义新的类型名
    typedef unsigned int WeThinkIn_int;
    typedef char* WeThinkIn_point;
    //2-为自定义数据类型(结构体、共用体和枚举类型)定义简洁的类型名称
    typedef struct target_Object{
        int x;
        int y;
    } WeThinkIn_Object;
    

    C/C++中面向对象的相关知识

    • 面向对象三大特征:封装,继承,多态
    • 封装:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。public成员:可以被任意实体访问;protected成员:只允许被子类及本类的成员函数访问;private成员:只允许被本类的成员函数、友元类或友元函数访问
    • 继承:基类(父类)-->派生类(子类)
    • 多态:即多种状态,可以将多态定义为消息以多种形式显示的能力,多态以封装和继承为基础
    • 多态的分类和实现:
    • 1-重载多态(Ad-hocPloymorphism, 编译器):函数重载、运算符重载
    • 2-子类型多态(Subtype Polymorphism,运行期):虚函数
    • 3-参数多态性(Parametric Polymorphism,编译器):类模板、函数模板
    • 4-强制多态(Coercion Polymorphism,编译器/运行期):基本类型转换、自定义类型转换

    C/C++中struct的内存对齐与内存占用计算?

    • 内存对齐:计算机系统对基本类型数据在内存中存放的位置有限,他们会要求这些数据的首地址的值是有效对齐值的倍数
    • 有效对齐值:默认对齐系数n,可以通过#pragma pack(n)指定,有效对齐值 = min(对齐系数n, 结构体中最长的数据类型)
    • 为什么要进行内存对齐:当没有内存对齐,数据可以任意存储,当有内存对齐时,比如4字节存取粒度的处理器,提高数据存取速度
    • struct内存对齐规则
    • 1-数据对齐规则:当第一个数据成员是int,第二个是double,有效对齐值为8
    • 2-结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部有效对齐值的整数倍地址开始存储
    • 3-结构体内存的总大小,必须是其有效对齐值的整数倍,补足的要补齐
    //struct的内存占用为40bytes
    #include <stdio.h>
    #pragma pack(8)
    int main()
    {
      struct Test
      {
        int a;
        //long double大小为16bytes
        long double b;         
        char c[10];
      };
      printf("%d", sizeof(Test));
      return 0;
    } 
    
    //struct的内存占用为48bytes
    #include <stdio.h>
    #pragma pack(16)
    int main()
    {
      struct Test
      {
        int a;
        //long double大小为16bytes
        long double b;         
        char c[10];
      }
      printf("%d", sizeof(Test));
      return 0;
    }
    

    C/C++中智能指针的定义与作用?

    • 智能指针是一个类,这个类的构造函数中传入一个普通指针,析构函数中释放传入的指针。智能指针的类都是栈上的对象,所以当函数(或程序)结束时会自动被释放
    • 常用的智能指针:智能指针在C++11版本之后提供,包含在头文件中,主要是shared_ptr、unique_ptr、weak_ptr。unique_ptr不支持复制和赋值。当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果原来的unique_ptr 将存在一段时间,编译器将禁止这么做。shared_ptr是基于引用计数的智能指针。可随意赋值,直到内存的引用计数为0的时候这个内存会被释放。weak_ptr能进行弱引用。引用计数有一个问题就是互相引用形成环,这样两个指针指向的内存都无法释放。需要手动打破循环引用或使用weak_ptr。顾名思义,weak_ptr是一个弱引用,只引用,不计数。如果一块内存被shared_ptr和weak_ptr同时引用,当所有shared_ptr析构了之后,不管还有没有weak_ptr引用该内存,内存也会被释放。所以weak_ptr不保证它指向的内存一定是有效的,在使用之前需要检查weak_ptr是否为空指针。
    • 智能指针的作用:C++11中引入了智能指针的概念,方便管理堆内存。使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,野指针,程序发生异常时内存泄露等问题等,使用智能指针能更好的管理堆内存。

    C/C++中程序的开发流程?

    • 开发流程:编辑、编译、链接、运行和调试
    • 编辑(.cpp):程序文本的输入和修改,头文件.h
    • 编译:使用编译器将C++高级语言“翻译”成汇编语言
    • 汇编(.obj):将汇编语言翻译成机器语言指令(二进制)
    • 链接(.o):将编译后产生的一个或多个目标文件与程序中用到的库文件链接起来,形成一个可以在操作系统中直接运行的可执行程序
    • 运行与调试:执行程序,出现问题进行debug

    C/C++中数组和链表的优缺点?

    • 1-数组:在内存中,数组是一块连续的区域,并且数组需要预留空间
    • 2-链表:在内存中,元素的空间可以在任何地方,空间是分散的,不需要连续,链表元素有两个属性,一个是元素的值,另一个是指针,指针标记了下一个元素的地址,通过地址查找下一个元素
    • 3-数组的优点:查询效率高,时间复杂度是O(1)
    • 4-数组的缺点:新增和修改效率低,时间复杂度为O(N),内存分配是连续的内存,扩容需要重新分配内存
    • 5-链表的优点:新增和修改效率高,只需要修改指针的指向,时间复杂度可以达到O(1);内存分配不需要连续的内存,占用连续内存少
    • 6-链表的缺点:查询效率低,需要从链表的头依次查找,时间复杂度为O(N)

    C/C++中的new和malloc有什么区别?

    • 1-malloc和free是标准库函数,支持覆盖;new和delete是运算符,支持重载
    • 2-malloc仅仅分配内存空间,free仅仅回收内存空间,不具备调用构造函数和析构函数功能,用malloc分配空间存储类的对象存在风险;new和delete除了分配回收功能外,还会调用构造函数和析构函数
    • 3-malloc和free返回的是void类型指针(必须进行类型转换),new和delete返回的是具体类型指针

    C/C++中结构体的区别?

    • C中的结构体只涉及到数据结构,而不涉及到算法,也就是说在C中数据结构和算法是分离的,而到C++中一类或者一个结构体可以包含函数(这个函数在C++我们通常中称为成员函数),C++中的结构体和类体现了数据结构和算法的结合

    • C中的结构体和C++中结构体的不同之处:在C中的结构体只能自定义数据类型,结构体中不允许有函数,而C++中的结构体可以加入成员函数。

    C++中的结构体和类的异同

    • 一、相同之处:结构体中可以包含函数;也可以定义public、private、protected数据成员;定义了结构体之后,可以用结构体名来创建对象。但C中的结构体不允许有函数;也就是说在C++当中,结构体中可以有成员变量,可以有成员函数,可以从别的类继承,也可以被别的类继承,可以有虚函数。

    • 二、不同之处:结构体定义中默认情况下的成员是public,而类定义中的默认情况下的成员是private的。类中的非static成员函数有this指针,(而struct中没有是错误的,一直被误导啊,经过测试struct的成员函数一样具有this指针),类的关键字class能作为template模板的关键字 即template<class T> class A{}; 而struct不可以。

    C/C++中的namespace

    • 命名空间作为附加信息来区分不同库中相同名称的函数、类、变量等。使用命名空间即定义了上下文,本质上,命名空间就是定义了一个范围
    • 命名空间可以嵌套
    #include <iostream>
    using namespace std;
    
    //第一个命名空间
    namespace first_space {
        void func() {
            cout << "Inside first_space" << endl;
        }
        //第二个空间
        namespace second_space {
            void func() {
                cout << "Inside second_space" << endl;
            }
        }
    }
    
    using namespace first_space::second_space;
    
    int main()
    {
        //调用第二个命名空间中的函数
        func();
        return 0;
    }
    

    Python中assert(断言)的作用?

    • assert 用于判断一个表达式,在表达式条件为false时触发异常
    • 断言在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃
    assert True
    assert False
    
    ---------------------------------------------------------------------------
    
    AssertionError                            Traceback (most recent call last)
    
    ~\AppData\Local\Temp/ipykernel_3860/2576994315.py in <module>
          1 assert True
    ----> 2 assert False
    
    
    AssertionError: 
    
    assert 1 == 1
    assert 1 != 2
    assert 1 == 2
    
    ---------------------------------------------------------------------------
    
    AssertionError                            Traceback (most recent call last)
    
    ~\AppData\Local\Temp/ipykernel_3860/2410673052.py in <module>
          1 assert 1 == 1
          2 assert 1 != 2
    ----> 3 assert 1 == 2
    
    
    AssertionError: 
    

    Python中互换变量有不用创建临时变量的方法吗?

    • 首先一般情况下Python是从左到右解析一个语句的,但在赋值操作的时候,因为是右值具有更高的计算优先级,所以需要从右向左解析。
    • 先计算右值y,x(这里是简单的原值,但可能会有表达式或者函数调用的计算过程), 在内存中创建元组(tuple),存储,y,x分别对应的值;
    • 计算左边的标识符,元组被分别分配给左值,通过解包(unpacking),元组中第一个标示符对应的值(y),分配给左边第一个标示符(x),元组中第二个标示符对应的值(x),分配给左边第二个标示符(y),完成了x和y的值交换。
    a, b = 1, 2
    a, b = b, a
    print(a, b)
    
    2 1
    

    Python中的主要数据结构都有哪些?

    • 列表(list)
    • 元组(tuple)
    • 字典(dict)
    • 集合(set)

    Python中的可变对象和不可变对象?

    • 可变对象和不可变对象区别在于对象本身是否可变
    • 可变对象:list(列表) dict(字典) set(集合)
    • 不可变对象:tuple(元组) string(字符串) int(整型) float(浮点型) bool(布尔型)

    Python中的None代表什么?

    • None是一个特殊的常量,表示空值,和 False, 0,空字符串不同,它是一个特殊的Python对象,None的类型是NoneType
    • None和任何其它数据类型比较返回False
    print(None == 0)
    print(None == ' ')
    print(None == False)
    print(None == None)
    
    False
    False
    False
    True
    

    Python中∗args和∗∗kwargs的区别?

    • 在函数定义时,将不定数量的参数传递给一个函数
    • *args将一个非键值对的可变数量的参数列表传递给函数
    • **kwargs
    def test_var_args(f_arg, *args):
        print("first normal arg: ", f_arg)
        for arg in args:
            print("another arg through *args: ", arg)
    test_var_args("hello", "python", "ddd", "test")
    
    first normal arg:  hello
    another arg through *args:  python
    another arg through *args:  ddd
    another arg through *args:  test
    
    def test_var_kwargs(**kwargs):
        for key, value in kwargs.items():
            print("{0} == {1}".format(key, value))
    test_var_kwargs(name = "kkwww", age = 18, weight = "60kg")
    
    name == kkwww
    age == 18
    weight == 60kg
    

    Python中Numpy的broadcasting机制?

    • 根据计算情况扩展数组的维度,自动填充数据
    import numpy as np
    a = np.array([1, 2, 3])
    b = np.array([6, 6, 6])
    print(a + b)
    
    c = a + 5
    print(c)
    
    d = np.arange(3).reshape(3, 1)
    print(a + d)
    
    [7 8 9]
    [6 7 8]
    [[1 2 3]
     [2 3 4]
     [3 4 5]]
    

    Python中的实例方法、静态方法和类方法三者区别?

    • 类方法:用@classmethod修饰,至少包含cls参数,调用类方法时不需要显式为cls参数传参
    • 静态方法:用@staticmethod修饰,没有类似cls和self这样的参数,因此Python的解释器不会对它包含的参数做任何类和对象的绑定
    • 实例方法:不用@classmethod和@staticmethod修饰,类中定义的方法默认为实例方法,特点是需要包含一个self参数,用于绑定调用此方法的实例对象

    Python中常见的切片操作

    • 切片是一个左闭右开区间[:] -> [:)
    example = [1, 2, 3, 4, 5]
    print(example[:3])
    print(example[3:])
    print(example[::-1])
    print(example[2::-1])
    
    [1, 2, 3]
    [4, 5]
    [5, 4, 3, 2, 1]
    [3, 2, 1]
    

    Python中如何进行异常处理?

    • 当程序无法正常处理程序时会引发异常,异常在python中是一个对象,表示一个错误。当Python发生异常,需要捕获,否则程序会终止执行
    try:
        6688 / 0
    except:
        '''异常的父类,可以捕获所有的异常'''
        print("0不能被除")
    else:
        '''保护不抛出异常的代码'''
        print("没有异常")
    finally:
        print("最后总是要执行我")
    
    0不能被除
    最后总是要执行我
    

    Python中remove,del以及pop之间的区别?

    • 相同点:删除列表、字符串等里面的元素
    • 1-remove剔除第一个匹配的值
    • 2-del通过索引删除元素
    • 3-pop通过索引删除元素,默认删除最后一个元素
    a = [i for i in range(10)]
    a.remove(1)
    print(a)
    del a[0]
    print(a)
    a.pop(2)
    print(a)
    
    [0, 2, 3, 4, 5, 6, 7, 8, 9]
    [2, 3, 4, 5, 6, 7, 8, 9]
    [2, 3, 5, 6, 7, 8, 9]
    

    Python中迭代器的概念?

    • 可迭代对象是迭代器、生成器和装饰器的基础。简单来说,可以用for循环遍历的对象就是可迭代对象,比如 list,set,dict
    • 可以通过 iter(object)来返回一个迭代器
    • 所有实现了iternext两个方法的对象,都是迭代器
    • Python的 for 循环本质上就是通过不断调用next()函数实现的
    from collections import Iterable
    print(isinstance("abcsde", Iterable))
    print(isinstance([1, 2, 3, 4, 5, 6], Iterable))
    print(isinstance(123456, Iterable))
    
    x = [1, 2, 3]
    y = iter(x)
    print(type(x))
    print(type(y))
    
    print(next(y))
    print(next(y))
    print(next(y))
    print(next(y))
    
    True
    True
    False
    <class 'list'>
    <class 'list_iterator'>
    1
    2
    3
    
    ---------------------------------------------------------------------------
    
    StopIteration                             Traceback (most recent call last)
    
    ~\AppData\Local\Temp/ipykernel_3860/363111041.py in <module>
         12 print(next(y))
         13 print(next(y))
    ---> 14 print(next(y))
    
    
    StopIteration: 
    

    Python中生成器的相关知识

    • Python在创建列表时,收到内存限制,容量有限,不可能全部一次性枚举,Python常用列表生成式有一个致命缺点就是定义即生成,非常浪费空间和效率
    • 生成器 generator: 在循环中不断推算出后续元素,不必创建完整的list,节省大量空间
    • yield:定义生成器函数,使用yield来支持迭代器协议
    • python支持协程,也就是微线程,通过generator来实现
    a = [x*x for x in range(10)]
    print(a)
    b = (x*x for x in range(10))
    print(type(b))
    
    def spam():
        yield"first"
        yield"second"
        yield"third"
    for x in spam():
        print(x)
    
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    <class 'generator'>
    first
    second
    third
    

    Python中装饰器的相关知识

    • 装饰器允许将现有函数传递给装饰器,从而向现有函数中添加额外的功能,装饰器将执行现有函数和添加的额外功能
    • 装饰器本身就是一个函数,可以在不改变已有函数的情况下增加功能
    • 如果需要给当前函数添加功能,重写函数使代码不能复用
    • 通过@语法糖来起到bar = use_log(bar)的作用,使用高级封装
    def foo():
        print("I am foo")
    def foo():
        print("foo is running")
        print("I am foo")
    
    # 改写
    import logging
    def use_log(func):
        logging.warning("%s is running" % func.__name__)
        func()
    def bar():
        print("I am bar")
        
    #将函数作为参数传入新定义函数
    use_log(bar)
    
    WARNING:root:bar is running
    
    
    I am bar
    
    import logging 
    def use_log(func):
        def wrapper(*args, **kwargs):
            logging.warning("%s is running" % func.__name__)
            return func(*args, **kwargs)
        return wrapper
    
    def bar():
        print("I am bar")
    
    bar = use_log(bar)
    bar()
    
    WARNING:root:bar is running
    
    
    I am bar
    
    import logging 
    def use_log(func):
        def wrapper(*args, **kwargs):
            logging.warning("%s is running" % func.__name__)
            return func(*args, **kwargs)
        return wrapper
    
    @use_log
    def bar():
        print("I am bar")
    
    @use_log
    def haha():
        print("I am haha")
        
    bar()
    haha()
    
    WARNING:root:bar is running
    WARNING:root:haha is running
    
    
    I am bar
    I am haha
    

    Python的深拷贝与浅拷贝?

    • 变量赋值:只是给当前内存中的对象增加一个“标签”(改个名字)
    • 浅拷贝:创建一个新的对象,其内容是原对象中元素的引用(新对象与原对象内存中的子对象),常见的浅拷贝方法包括:切片操作,工厂函数,对象的copy()方法,copy模块中的copy函数
    • 深拷贝:创建一个新的对象,然后递归的拷贝原对象所包含的子对象。深拷贝出来的对象与原对象没有任何关联。深拷贝只有一种方式,copy模块中的deepcopy()函数
    # 变量赋值
    a = [1, 2, 3, 4, 5]
    b = a
    print(id(a), id(b))
    print(a == b)
    
    1868670559168 1868670559168
    True
    
    #浅拷贝
    a = [4, 3, 2]
    b = list(a)
    print(id(a), id(b))      #a和b的地址不同
    print("------------------")
    for x, y in zip(a, b):
        print(id(x), id(y))  #他们的子对象地址相同
    
    1868672945792 1868690687168
    ------------------
    1868592736656 1868592736656
    1868592736624 1868592736624
    1868592736592 1868592736592
    
    import copy
    #深拷贝
    a = [[1, 2],
        [3, 4],
        [5, 6]]
    b = copy.copy(a)  #浅拷贝
    c = copy.deepcopy(a) #深拷贝
    print(id(a), id(b), id(c))
    print("---------------------")
    for x, y in zip(a, b):
        print(id(x), id(y))
    print("----------------------")
    for x, y in zip(a, c):
        print(id(x), id(y))
    
    1868690646400 1868670536832 1868668928256
    ---------------------
    1868690461952 1868690461952
    1868690720896 1868690720896
    1868690647872 1868690647872
    ----------------------
    1868690461952 1868670467968
    1868690720896 1868690461824
    1868690647872 1868690720384
    

    Python是解释语言还是编译语言?

    • Python是解释性语言
    • 解释语言优点是可移植性好,缺点是运行需要解释环境,运行起来比编译语言慢,占用资源多,代码效率低
    • 编译语言的优点是运行速度快,代码效率高,编译之后程序不能修改,保密性好。缺点是代码需要经过编译才能运行,可移植性较差,只能在兼容的操作系统上运行

    Python的垃圾回收机制

    • 在Python中,使用引用计数进行垃圾回收
    • 同时通过标记-清除算法解决容器对象可能产生的循环引用问题
    • 最后通过分代回收算法提高垃圾回收效率

    Python里有多线程吗?

    • Python中的多线程是假的多线程
    • Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核,只有一个线程在解释器中运行

    Python中range和xrange的区别?

    • 二者用法相同,不同之处在于xrange函数生成一个生成器
    • 在python3中,xrange被移除

    Python中列表和元组的区别?

    • 列表是可变的,在创建之后可以对其进行任意修改
    • 元组是不可变的,一旦创建不可改变
    • 元组无法复制
    • Python将低开销的较大的块分配给元组,因为它们是不可变的。对于列表则分配小内存块。与列表相比,元组的内存更小。当你拥有大量元素时,元组比列表快。

    Python中dict(字典)的底层结构?

    • 为了支持快速查找,使用哈希表作为底层结构,哈希表查找的平均时间复杂度是O(1)
    • CPython使用二次探查解决哈希冲突问题

    常用的深度学习框架有哪些,都是哪家公司开发的?

    • PyTorch:Facebook
    • TensorFlow:Google
    • Keras:Google
    • MxNet:Dmlc社区
    • Caffe:UC Berkeley
    • PaddlePaddle:百度

    PyTorch动态图和TensorFlow静态图的区别?

    • PyTorch动态图:计算图的运算与搭建同时进行;比较灵活,方便调节
    • TensorFlow静态图:计算图时先搭建图,后运算;比较高效,不灵活

    相关文章

      网友评论

          本文标题:C/C++/Python基本知识总结

          本文链接:https://www.haomeiwen.com/subject/bkysddtx.html