Python对象众所周知,Python是一门面向对象的语言,在Python无论是数值、字符串、函数亦或是类型、类,都是对象 。
对象是在 堆 上分配的结构,我们定义的所有变量、函数等,都存储于堆内存,而变量名、函数名则是一个存储于 栈 中、指向堆中具体结构的引用 。
要想深入学习Python,首先需要知道Python对象的定义 。
我们通常说的Python都是指CPython,底层由C语言实现 , 源码地址: cpython [GitHub]
Python对象的定义位于 Include/object.h,是一个名为 PyObject 的结构体:
Python中的所有对象都继承自PyObejct,PyObject包含一个用于垃圾回收的双向链表 , 一个引用计数变量 ob_refcnt 和 一个类型对象指针 ob_type
从PyObejct的注释中,我们可以看到这样一句:每个指向 可变大小Python对象 的指针也可以转换为 PyVarObject* (可变大小的Python对象会在下文中解释) 。PyVarObejct 就是在PyObject的基础上多了一个 ob_size 字段,用于存储元素个数:
在PyObject结构中,还有一个类型对象指针 ob_type ,用于表示Python对象是什么类型 , 定义Python对象类型的是一个 PyTypeObject 接口体
实际定义是位于 Include/cpython/object.h 的 _typeobject :
在这个类型对象中,不仅包含了对象的类型,还包含了如分配内存大小、对象标准操作等信息,主要分为:
以Python中的 int类型 为例,int类型对象的定义如下:
从PyObject的定义中我们知道,每个对象的 ob_type 都要指向一个具体的类型对象,比如一个数值型对象 100,它的ob_type会指向 int类型对象PyLong_Type。
PyTypeObject结构体第一行是一个PyObject_VAR_HEAD宏,查看宏定义可知PyTypeObject是一个变长对象
也就是说 , 归根结底 类型对象也是一个对象 , 也有ob_type属性,那 PyLong_Type 的 ob_type 是什么呢?
回到PyLong_Type的定义,第一行 PyVarObject_HEAD_INIT(PyType_Type, 0),查看对应的宏定义
由以上关系可以知道,PyVarObject_HEAD_INIT(PyType_Type, 0)={ { _PyObject_EXTRA_INIT 1, PyType_Type } 0} , 将其代入 PyObject_VAR_HEAD , 得到一个变长对象:
这样看就很明确了,PyLong_Type的类型就是PyType_Typ,同理可知,Python类型对象的类型就是PyType_Type ,而 PyType_Type对象的类型是它本身
从上述内容中,我们知道了对象和对象类型的定义,那么根据定义,对象可以有以下两种分类
Python对象定义有 PyObject 和 PyVarObject ,因此,根据对象大小是否可变的区别,Python对象可以划分为 可变对象(变长对象) 和 不可变对象(定长对象)
原本的对象a大小并没有改变,只是s引用的对象改变了 。这里的对象a、对象b就是定长对象
可以看到,变量l仍然指向对象a,只是对象a的内容发生了改变,数据量变大了 。这里的对象a就是变长对象
由于存在以上特性,所以使用这两种对象还会带来一种区别:
声明 s2 = s,修改s的值: s = 'new string' ,s2的值不会一起改变,因为只是s指向了一个新的对象,s2指向的旧对象的值并没有发生改变
声明 l2 = l,修改l的值: l.append(6) ,此时l2的值会一起改变 , 因为l和l2指向的是同一个对象 , 而该对象的内容被l修改了
此外 , 对于 字符串 对象 , Python还有一套内存复用机制 , 如果两个字符串变量值相同 , 那它们将共用同一个对象:
对于 数值型 对象 , Python会默认创建0~2 8以内的整数对象 , 也就是 0 ~ 256 之间的数值对象是共用的:
按照Python数据类型 , 对象可分为以下几类:
Python创建对象有两种方式,泛型API和和类型相关的API
这类API通常以 PyObject_xxx 的形式命名,可以应用在任意Python对象上,如:
使用 PyObjecg_New 创建一个数值型对象:
这类API通常只能作用于一种类型的对象上,如:
使用 PyLong_FromLong 创建一个数值型对象:
在我们使用Python声明变量的时候,并不需要为变量指派类型,在给变量赋值的时候,可以赋值任意类型数据 , 如:
从Python对象的定义我们已经可以知晓造成这个特点的原因了,Python创建对象时,会分配内存进行初始化,然后Python内部通过 PyObject* 变量来维护这个对象,所以在Python内部各函数直接传递的都是一种泛型指针 PyObject* , 这个指针所指向的对象类型是不固定的,只能通过所指对象的 ob_type 属性动态进行判断,而Python正是通过 ob_type 实现了多态机制
Python在管理维护对象时,通过引用计数来判断内存中的对象是否需要被销毁,Python中所有事物都是对象,所有对象都有引用计数 ob_refcnt。
当一个对象的引用计数减少到0之后,Python将会释放该对象所占用的内存和系统资源 。
但这并不意味着最终一定会释放内存空间 , 因为频繁申请释放内存会大大降低Python的执行效率 , 因此Python中采用了内存对象池的技术,是的对象释放的空间会还给内存池,而不是直接释放 , 后续需要申请空间时,优先从内存对象池中获取 。
python 有宏定义,或者枚举么如果那样就如你所说搞一个"头文件",不过在python中可是一个模块文件,里面定义了常量(其实还是变量):如叫constants.pycomplete=12fail=13使用时import
constants
或from
constants
import
*这样就行了 。
python中random什么意思Random意思是返回一个0~num-1之间的随机数 。random(num)是在stdlib.h中的一个宏定义 。num和函数返回值都是整型数 。
如需要在一个random()序列上生成真正意义的随机数,在执行其子序列时使用randomSeed()函数预设一个绝对的随机输入,例如在一个断开引脚上的analogRead()函数的返回值 。
Random的作用
Random使用之前需要使用Randomize语句进行随机数种子的初始化 。RANDOM产生的是伪随机数或者说是用一种复杂的方法计算得到的序列值,因此每次运算时需要一个不同的种子值 。种子值不同,得到的序列值也不同 。因此也就是真正的随机数了 。
RANDOM产生的是伪随机数或者说是用一种复杂的方法计算得到的序列值 , 因此每次运算时需要一个不同的种子值 。种子值不同 , 得到的序列值也不同 。因此也就是真正的随机数了 。这也正是RANDOMIZE随机初始化的作用 。VB里用 NEW RANDOM()来表示初始化 。
sqrt(2)*x和sqrt2*x的区别sqrt(2)*x和sqrt2*x的区别是一个非负实数的平方根函数原型 在VC6.0中的math.h头文件的函数原型为double sqrt(double);说明:sqrt是C语言的库函数 , 声明于math.h中 。形式为doublesqrt(doublen)功能为求n的算术平方根 , 并返回 。当n值非法(n0)时会出错 。
sqr在C语言中没有明确定义,不属于库函数或关键字 。如果在C语言中出现sqr,那么可能是自定义函数或宏定义 。不过在一些其它语言中,sqr是用来求算数平方根的,类似于C的sqrt 。在Pascal中,sqr为求某数的平方 。从这个角度上看,在C中如果出现sqr那么也应是求平方根或者求平方,具体含义还要看如何实现 。sqrt系Square Root Calculations(平方根计算),通过这种运算可以考验CPU的浮点能力 。Python函数#!/usr/bin/env pythonimport math # This will import math moduleprint("***.sqrt(100) is:", ***.sqrt(100)),
Python中定义函数的使用方法?4.6. 定义函数
我们可以创建一个用来生成指定边界的斐波那契数列的函数:
def fib(n):# write Fibonacci series up to n
..."""Print a Fibonacci series up to n."""
...a, b = 0, 1
...while an:
...print(a, end=' ')
...a, b = b, a b
...print()
...
# Now call the function we just defined:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
关键字 def 引入了一个函数 定义 。在其后必须跟有函数名和包括形式参数的圆括号 。函数体语句从下一行开始,必须是缩进的 。
函数体的第一行语句可以是可选的字符串文本,这个字符串是函数的文档字符串,或者称为 docstring 。(更多关于 docstrings 的信息请参考 文档字符串) 有些工具通过 docstrings 自动生成在线的或可打印的文档,或者让用户通过代码交互浏览;在你的代码中包含 docstrings 是一个好的实践 , 让它成为习惯吧 。
函数 调用 会为函数局部变量生成一个新的符号表 。确切的说,所有函数中的变量赋值都是将值存储在局部符号表 。变量引用首先在局部符号表中查找 , 然后是包含函数的局部符号表 , 然后是全局符号表 , 最后是内置名字表 。因此 , 全局变量不能在函数中直接赋值(除非用 global 语句命名),尽管他们可以被引用 。
函数引用的实际参数在函数调用时引入局部符号表,因此,实参总是 传值调用 (这里的 值 总是一个对象 引用,而不是该对象的值) 。[1] 一个函数被另一个函数调用时,一个新的局部符号表在调用过程中被创建 。
一个函数定义会在当前符号表内引入函数名 。函数名指代的值(即函数体)有一个被 Python 解释器认定为 用户自定义函数 的类型 。这个值可以赋予其他的名字(即变量名),然后它也可以被当作函数使用 。这可以作为通用的重命名机制:
fib
f = fib
f(100)
0 1 1 2 3 5 8 13 21 34 55 89
如果你使用过其他语言 , 你可能会反对说:fib 不是一个函数,而是一个方法,因为它并不返回任何值 。事实上 , 没有 return 语句的函数确实会返回一个值,虽然是一个相当令人厌烦的值(指 None ) 。这个值被称为 None (这是一个内建名称) 。如果 None 值是唯一被书写的值,那么在写的时候通常会被解释器忽略(即不输出任何内容) 。如果你确实想看到这个值的输出内容,请使用 print() 函数:
如何用Python封装C语言的字符串处理函数在C语言中,字符串处理是每天都要面对的问题 。我们都知道C语言中其实并没有一种原生的字符串类型 , ‘字符串’在C语言里只是一种特殊的以''结尾的字符数组 。因此,如何将C语言与更高层次的Python语言在‘字符串’处理这个问题上对接是一个有难度的问题 。所幸有swig这种强大的工具 。
如何封装一个函数,它修改参数字符串的内容
假如有这样一个C语言的函数,
!-- lang: cpp --
void FillZero(char* pc,size_t * piLen)
{
size_t i=0;
while(i*piLen/2 )
*pc= '0';
*pc = 0;
*piLen = i 1;
}
这个函数的功能是把字符串变成n个0 。不过我们更关注函数的形式 。这样的函数,表面上看char* pc是函数的参数,可是实际上它才是函数的返回值和执行的结果 。piLen这个参数既是pc的最大长度,也是新的字符串的长度 。我们直接用python封装,看看运行结果 。
Type "help", "copyright", "credits" or "license" for more information.
import cchar
s='123456'
cchar.FillZero(s,6)
Traceback (most recent call last):
File "stdin", line 1, in module
TypeError: in method 'FillZero', argument 2 of type 'size_t *'
结果差强人意,不是我们想要得到的结果 。函数的第二个参数为size_t* 我们很难用python来表示,而且python中也不存在既是输入 , 也是输出的参数 。
swig有一个标准库,其中有一个cstring.i文件就是用来解决C语言字符串类型的问题 。
我们在.i文件中加入这样几行
!-- lang: cpp --
%include "cstring.i"
%cstring_output_withsize(char* pc,size_t* pi)
void FillZero(char* pc, size_t* pi);
然后运行看结果
Type "help", "copyright", "credits" or "license" for more information.
import cchar
cchar.FillZero(10)
'00000\x00'
s=cchar.FillZero(10)
print s
00000
我们看函数的变化 。首先在python里, FillZero变成了只有一个参数的函数 。然后函数的返回值变成了一个字符串 。其实cstring_output_size其实是一个宏,通过这个宏的定义改变了函数的形式,直接在Python中得到我们想要的结果 。
其实类似cstring_output_size的宏还有好几个 , 我列举一下:
cstring_output_allocate(char *s,free($1));
第一个参数是指向字符串地址的指针,第二个参数为释放空间的方法 。
大家考虑这一下这样的函数:
void foo(char*s)
{
s = (char*)malloc(10);
memcpy(s,"123456789",9);
}
s这个参数表面上看是输入,实际上是函数真正的输出 。函数中真正改变的东西是chars指向的字符串的值 。而且char这个类型,
python或者其他脚本语言里应该都没有对应的类型 。那么我们用cstring_output_allocate将这个函数转换成另外一个形式的python或者其他脚本语言的函数 。转换后的函数其实是这样的,以python为例str
foo() 。
!-- lang: cpp --
%module a
%include "cstring.i"
%{
void foo(char* s);
%}
%cstring_output_allocate(char *s, free(*$1));
void foo(char *s);
在python中的调用:
!-- lang: python --
import a
a.foo()
'123456789'
【python中宏定义函数 python中的宏定义】cstring_output_maxsize(char *path, int maxpath);
第一个参数也是可以改变的字符串首地址,第二个参数为字符串的最大长度 。在Python中调用的时候,只有maxpath这个参数,返回字符串 。
cstring_output_allocate(char *s, free($1));
第一个参数为指向字符串首地址的指针,第二个参数为释放指针的方法 。这个宏主要是封装一种直接在函数内部malloc空间的函数 。在Python中调用时没有参数,直接返回字符串 。
cstring_output_allocate_size(char *s, int slen, free(*$1));
这个相当于前面两个函数的组合 。在函数内部malloc空间,然后将字符串长度通过slen返回 。其实在调用的时候非常简单,没有参数,直接返回字符串 。
如何处理c的std::string
std::string是C标准类库STL中常见的类 。在平时工作中大家肯定是没少用 。在python中如何封装std::string? swig提供了标准库
例如函数:
!-- lang: cpp --
string Repeat(const string s)
{
return s s;
}
只要在swig中加入这样几行:
!-- lang: cpp --
%include "std_string.i"
using namespace std;
string Repeat(const string s);
运行结果:
Python 2.6.6 (r266:84292, Dec 27 2010, 00:02:40)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
import cchar
cchar.Repeat('123')
'123123'
使用起来很方便,但需要注意的是 , 假如函数的参数的内容是可以被修改,就不能用这种方式封装 。
例如:
!-- lang: cpp --
void repeat(string s)
{
s =s;
}
这样的函数直接使用 'std_string.i' 就是无效的 。遇到这种函数,只能用C语言封装成 void repeat(chars, int maxsize), 再用swig调用 'cstring_output_withsize' 这个宏再封装一次了 。
关于python中宏定义函数和python中的宏定义的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。
推荐阅读
- flutter框架极光push,flutter 推送插件
- 读研需要什么电脑技能证,读研需要用电脑吗
- css去掉下拉边框,css去掉边框线
- 像素风格飞行游戏,像素飞机
- 树莓派php数据分析 树莓派sql
- ChatGPT4训练数据多大,ChatGPT4训练数据多大
- 网络能发消息玩不了游戏,为什么网络可以回消息却不能打游戏
- iis7wordpress伪静态规则,iis10伪静态
- asp.net取字符后面的,net字符串转为数字