1.python list=[1,2,3,4,5,6,7,8] tuple=(1,2,3,4,5,6,7,8) list[0::2] 和 tuple(0::2) 的结果分别是什么?
2.C++ の 内存管理(二)std::unique_ptr源码浅析
3.x,y 都是int, x++=y++ 为什么错?
4.python的库,比如numpy是如何调用c语言实现的代码的?
5.spark sql源码系列 | json_tuple一定比 get_json_object更高效吗?
python list=[1,2,3,4,5,6,7,8] tuple=(1,2,3,4,5,6,7,8) list[0::2] 和 tuple(0::2) 的结果分别是什么?
其实你最好安装环境自己试一下,这样能得到结果,也能加深对python的理解:In [1]: a=[1,2,3,4,5,6,7,8]In [2]: b=(1,2,3,4,5,6,7,8)
In [3]: a[0::2]
Out[3]: [1, 3, 5, 7]
In [4]: b[0::2]
Out[4]: (1, 3, 5, 7)
In [5]:
因为list和tuple是python的关键字,我替换成了a和b。
如果解决了您的问题请采纳!
如果未解决请继续追问
C++ の 内存管理(二)std::unique_ptr源码浅析
本文主要阐述了C++标准库中的鑫游源码unique_ptr内存管理机制。unique_ptr通过RAII(Resource Acquisition Is Initialization)原理,提供了一种自动内存管理方式。其内部实现关键在于一个tuple,结合raw pointer和自定义deleter,确保栈上指针生命周期结束后,自动释放堆内存。unique_ptr的独特之处在于它不可复制,只支持移动,确保内存所有权的单一性。
unique_ptr的核心是__uniq_ptr_impl类,它实现了raw pointer的所有操作,包括获取raw pointer、接受用户自定义deleter。std::make_unique的源码直观展示了如何通过new操作内存分配,然后将新分配的内存传递给unique_ptr的构造函数,整个过程简洁明了。
通过实例,我们可以看到unique_ptr在内存分配和释放上的优势。当使用make_unique时,它会调用new一次并分配内存,然后传递给unique_ptr,这样就只需要构造和析构各一次,实现了高效和安全的内存管理。
总结来说,unique_ptr是C++后引入的智能指针,它利用RAII封装内存管理,提供了在栈上对堆内存的自动释放功能,避免了内存泄漏问题。00101010源码通过unique_ptr,开发者可以更放心地进行内存操作,无需担心析构细节。
x,y 都是int, x++=y++ 为什么错?
赋值运算需要左值,而x++不是左值。x++的本意是使用x的值,在使用后将x自加1,那么如果把x++用在赋值运算的左边的话,赋值时是赋给原先的x还是自加后的x呢?显然是未定义的,所以这种操作是错误的。
下面转一篇关于左值和右值的文章,相信会对你帮助很大的。
左值(lvalue)和右值(rvalue)是编程中两个非常基本的概念,但是也非常容易让人误解,看了很多文章,自我感觉真正将这个问题讲的很透彻的文章还没有看见,所以自告奋勇来尝试一下。如果左值右值的概念不是非常清楚的话,它们迟早会像拦路虎一样跳出来,让你烦心不已,就像玩电脑游戏的时候每隔一段时间总有那么几个地雷考验你的耐性,如果一次把所有地雷扫尽就好了。:)
左值(lvalue)和右值(rvalue)最先来源于编译理论(感谢南大小百合的programs)。在C语言中表示位于赋值运算符两侧的两个值,左边的就叫左值,右边的就叫右值。比如:
int ii = 5;//ii是左值,5是右值
int jj = ii;//jj是左值,ii是右值
上面表明,左值肯定可以作为右值使用,但反之则不然。左值和右值的最早区别就在于能否改变。左值是filecoin 源码可以变的,右值不能变。注1
注1:这一点在C++中已经猪羊变色,不再成立。拱猪游戏还是挺好玩的,我还真抓过好几次全红心,不过真的好险。:)
在很多文章中提到,在C++中,左值更多的指的是可以定位,即有地址的值,而右值没有地址。注2
注2:这一点仍然不准确,我在程序中生成一个临时右值std::vector(),你能够说它没有地址吗?难道它是没有肉体的鬼魂或幽灵?它是有地址的,而且它也是绝对的右值。
在现代C++中,现在左值和右值基本上已经失去它们原本所具有的意义,对于左值表达式,通过具体名字和引用(pointer or reference)来指定一个对象。非左值就是右值。我来下一个定义:
左值表示程序中必须有一个特定的名字引用到这个值。
右值表示程序中没有一个特定的名字引用到这个值。
跟它们是否可以改变,是否在栈或堆(stack or heap)中有地址毫无关系。
1.左值
在下面的代码中:
int ii = 5;
int const jj = ii;
int a[5];
a[0] = ;
*(a+3) = ;
int const& max( int const& a, int const& b ) //call by reference
{
return a > b ? a : b;
}
int& fun(int& a) //call by reference
{
a += 5;
return a;
}
ii,jj,a[0],*(a+3),还有函数max的返回值比如max(ii, jj),注3函数fun的返回值fun(ii)都是左值。,它们都是有特定的引用名字的值。ii,mybais源码jj,a[0],*(a+3),max(ii, jj),fun(ii)分别就是它们的名字。
注3:在这里有一个不太容易分清楚的盲点。那就是有人会问max(8, 9)到达是左值还是右值,C++标准规定常量引用(reference to const)可以引用到右值,所以max(8, 9)似乎应该是右值,不过不管它是左值,还是右值,我们都不能试图去改变它。为了与前面的概念一致,我认为它是左值,不可改变的常量左值。
左值有不能改变的,即被const所修饰的左值,比如上面的jj,max(ii, jj)都是被常量(const)魔咒所困住的左值。
没有被const困住的左值当然是可以改变的,比如下面的代码都是成立的:
ii = ;
a[0] = ;
fun(ii) = ; //OK!
我们的眼睛没有问题,fun(ii) = ;完全正确,因为它是可以改变的左值。所以我们看STL的源码,就会理解std::vector中的重载operator[]运算符的返回值为什么要写成引用,因为operator[]必须返回左值。
2.右值
没有特定名字的值是右值。先看下面的代码:
std::list();
std::string(“It is a rvalue!”);
int fun1() //call by value
{
…
}
int* fun2() //call by reference
{
…
}
其中std::list(),std::string(“It is a rvalue!”),函数fun1的返回值fun1(),函数fun2的返回值fun2()都是右值,它们的值都没有特定的名字去引用。也许有人会奇怪,韭菜源码fun2()也是右值?最前面的max(a,b)不是左值吗?
请看清楚,函数fun2的返回值是pointer,pointer也是call by value,而函数max的返回值是reference,reference是call by reference。所以说C++中引入reference不仅仅是为了方便,它也是一种必须。注4
注4:Scott Meyer写的《More Effective C++》的条款1专门讲了pointer和reference的区别,写的很好,辨别的非常清楚。
fun2()是右值,但 *fun2()却是左值,就跟经常看到的*p一样,所以看C++库代码的时候,会发现重载operator*的函数返回值是reference。
当然我还遗漏了一种右值,那就是字面上的(literal)值,比如5,8.,’a’等等理所当然的都是右值。
右值最初出现的时候,一个最大的特征就是不可改变。但就跟我们的道德标准一样,时代不同了,标准也变化了,以前的三纲五常早已经被扔到历史的垃圾堆里面了。
C++中有可以改变的右值,而且这个特性还非常有用。那就是用户自定义的类(class)的构造函数生成的临时对象。比如:
std::vector(9),std::deque(),……都是可以改变的右值。在Herb Sutter的《More Exceptional C++》中的条款7的page页有这样几行代码:
// Example 7-2(b): The right way to shrink-to-fit a vector.
vector<Customer> c( );
// ...now c.capacity() >= ...
// erase all but the first elements
c.erase( c.begin()+, c.end() );
// the following line does shrink c's
// internal buffer to fit (or close)
vector<Customer>( c ).swap( c );
// ...now c.capacity() == c.size(), or
// perhaps a little more than c.size()
认真看几遍,你会发现但vector的大小增大到一定程度,你又用不着这么多空间的时候,你会想办法把它收缩到最合适的大小,但利用别的办法比如调用成员函数reserve()都无法办到,这个时候就必须利用右值可以改变这个性质了。
vector<Customer>( c ).swap( c );这行代码就是点睛之处。
首先使用复制构造函数生成临时右值vector<Customer>( c ),这个右值正好是合适大小,然后和c交换注5,c就变成合适大小了,最后在整个表达式结束的时候,这个临时右值析构归还内存空间。真是绅士一般的优雅!
注5:这个时候这个临时右值就发生了改变。
如果还不理解,可以看看书,或者直接看库的源代码。
至于为什么会这样?我思考了一下,我想是这样的,我们看类(class)的数据布置结构,会发现它的每一个数据成员都是有名字的,我想编译器在编译的过程中,都会生成一个外部不所知的对这个临时对象右值的名字引用,但需要改变这个临时对象的时候,这个名字就用上了。比如:
class Point
{
public: //纯粹为了方便,我把数据成员公开,现实中尽量不要这样用
int x, y ,z;
……//其他各种成员函数
};
我们现在就可以改变右值,用到了匿名的引用名字。
Point().x = 6;//改变了右值
Point().y = 6;//同意改变了右值,不过注意,这个右值跟上面的不是同一个。
总结
左值和右值的真正区别我想就是这些了,左值表示有特定的名字引用,而右值没有特定的名字引用。当然我仍然会有疏忽,希望大家能够提醒我,指正我的不足。
前两天看Herb Sutter从邮件中寄来的新文章(我订阅他的新文章邮件通知),一篇是讲Tuple数据结构的,没有什么新意,以前好像看过,还有一篇名字是:(Mostly)Private,地址为/documents/s=/cujcexpsutter/ 内容本身并不深,但看完文章,发现随处可见C++的波诡云谲,又会对什么叫袖里乾坤,滴水藏海多一份感性认识。
在下一篇文章我想从不同于一般的角度,从自己的经历谈谈在校毕业生在IT行业怎样找工作,我想会让所有读者都有一些思考,不仅仅是求职者。题目我已经想好了,就叫《扮虎吃猪》,不过现在我有一些别的事情要忙,所以可能会让大家等几天。
转载请注明来源,谢谢!
python的库,比如numpy是如何调用c语言实现的代码的?
在深入探讨Python的内置容器实现的过程中,我们将重点讲解PyListObject、PyTupleObject、PyDictObject和PySetObject的C语言实现及其相关API。在这一系列教程中,我们已探讨过Python的内置对象和优化了datetimecpy.date对象。欲了解详情,敬请访问教程的repo。
### PyListObject及其相关函数
Python的list底层实现为一段连续分配的内存,通过指针获取数据,类似数组实现。查看源码(Python 3.9)可知,`ob_item`用于存放元素,而`allocated`表示已分配内存。`ob_size`则用于存储实际长度,且必须小于`allocated`,强调了内存空间的高效重复利用。
在实现中,`ob_item`被解释为指针的指针,用户应将PyObject*视为整体,因为所有对Python对象的操作都是引用。在C语言环境下,Python对象数组即为指针的指针。
重点API包括访问、修改、遍历和管理list的方法。
### PyTupleObject及其相关函数
作为不可变序列容器,tuple底层实现与list极为相似,但其设计考虑了内存效率和不可变性。在Python 3.9的源码中,`ob_item`定义为数组,元素存储连续,体现数组特性。
由于不可变性,tuple无需动态分配内存,故无`allocated`字段。`ob_size`同样用于存储长度信息。
区别于list,tuple的API侧重于元素访问和不可变性维护。
### PyDictObject及其相关函数
Python字典基于哈希表实现,负载因子设置为2/3,确保高效查找。在Python 3.9版本中,dict有两种类型:combined和split。combined类型将key和value存放在同一entry内,split类型则将key和value分开存储,分别通过不同的机制进行管理。
为了节省内存,PyDictKeysObject在存储dk_indices时使用char数组,并通过DK_ENTRIES宏转换为PyDictKeyEntry,实现更紧凑的存储。
常用API包括插入、查找、更新和删除等操作。
### PySetObject及其相关函数
Python中的set基于哈希表实现,内部使用table存储元素。插入元素时,通过哈希值与mask进行与运算确定位置,若位置为空则直接插入,否则继续尝试插入以避免冲突。
API涵盖元素添加、查找、删除和迭代等基本操作。
### 操作实践与小结
在datetimecpy项目中,我们实现了timetuple函数,用于返回tuple对象,包含year、month、day、hour、minute、second、wday、yday和dst等元素。此函数利用了PyTupleObject相关API,实现了Python日期时间对象与tuple之间的转换。
本章旨在深入理解Python内置容器的C语言实现及其API,为后续构建datetime中的新对象——time对象打下基础。下一章将探讨模块和方法相关的C语言API。
spark sql源码系列 | json_tuple一定比 get_json_object更高效吗?
对比json_tuple和get_json_object,网上普遍认为json_tuple效率更高。理由是json_tuple仅需解析一次json数据,而get_json_object需多次解析。实际操作中,get_json_object在解析json字符串到jsonObject阶段仅执行一次,而非多次解析。从执行计划角度看,get_json_object更为简洁,而json_tuple涉及udtf函数,其执行计划更为繁重。功能多样性上,get_json_object支持更丰富的路径处理,如正则匹配、嵌套、多层取值等,而json_tuple仅能解析第一层key。在实际使用时,无需盲从效率结论,根据具体需求选择。确保json数据不过长过大,无论使用哪种方法,效率都不会理想。正确理解并合理运用这些函数,对于优化查询性能至关重要。