day01

变量

变量的命名应该满足以下三个规范:
1.变量的命名应该能反映变量值所描述的状态,切记不要用中文
2.变量名可由字母、数字和下划线组成,但是不能以数字开头
3.关键字不能声明为变量名

以下为常见的几个变量名

['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield']

变量体的两种风格

驼峰体

AgeOfCyk = 25
print(AgeOfCyk)

下划线体

age_of_Cyk = 25
print(age_of_Cyk)
25

变量的三个特征

x = 10
print(x)  # 获取变量的变量值
print(id(x))  # 获取变量的id,可以理解成变量在内存中的地址
print(type(x))  # 获取变量的数据类型,下章会详细介绍数据类型

#在Python中,is是一个用于比较两个对象身份(identity)的运算符。具体而言,is运算符用于检查两个对象是否引用了内存中的相同位置。当使用is运算符进行比较时,它会检查对象的内存地址,如果两个对象引用了同一个内存地址,则返回True,否则返回False。

x = "Hello"
y = "Hello"
z = x
print(x is y)  # True,x和y引用的是相同的字符串对象
print(x is z)  # True,x和z引用了相同的内存地址


a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)  # True,a和b的值相等
print(a is b)  # False,a和b引用了不同的内存地址
#在上面的示例中,a和b引用了不同的内存地址,所以a is b的结果是False。但是它们的值是相等的,所以a == b的结果是True。
10
1906339584
<class 'int'>
True
True
True
False
x = 257
z = 257

print(x is z)  # False
#在Python中,整数对象在范围-5到256之间被缓存,以便在代码中的多个引用中共享相同的对象。这意味着对于位于此范围内的整数,它们的id()是相同的,因此is运算符会返回True。然而,对于超过此范围的整数,Python不会缓存对象,因此它们的id()将不同,is运算符会返回False。此时变量x和z都被赋值为257。根据上述规则,它们的is运算符结果将是False,因为257超过了缓存范围。因此,print(x is z)的输出将是False。
False

常量

python中没有使用语法强制定义常量

python的变量内存管理

变量存放

x = 25

如果我们在python中定义了一个变量x,如果我们没有用python解释器去运行该文件,那么x=10只是很普通的四个字符x、=、1、0,只有当python解释器运行这个文件的时候,字符才会进入内存,才会有变量这个概念,也就是说变量是存放在内存中的。

eg:现在想象我们在学校(电脑内存)里上课,学校每开一个班,学校都会开辟一个教室给这个班级上课用(存放变量值10),而班级的门牌号则是(变量名x)。也就是说,对于电脑内存这个大内存,每定义一个变量就会在这个大内存中开辟一个小空间,小空间内存放变量值10,然后内存给这个小空间一个变量名x(门牌号),x指向10。

垃圾回收机制

如果我们此时再加上另一段代码x = 11,大内存会开辟另外一个小空间存储变量11,把变量值绑定另一个门牌号x,但是由于之前有x,所以大内存会解除x与10的连接,让x与11连接。这个时候10由于没有了门牌号,所以成为了python眼中的垃圾,python就会处理这个垃圾,释放10的内存占用,这就是python的垃圾回收机制。而其他编程语言需要手动把10的内存占用释放掉。

引用计数

在Python中,可以通过sys模块的getrefcount函数获取指定对象的引用计数器的值

import sys

class A():
    def __init__(self):
        pass
        
a = A()
print(sys.getrefcount(a))
2

计数器增减条件

引用次数+1的条件
  • 对象被创建,如A()。
  • 对象被引用,如a=A()。
  • 对象作为函数的参数,如func(a)。
  • 对象作为容器的元素,如arr=[a,a]。
引用次数-1的条件
  • 对象被显式销毁,如del a。
  • 变量重新赋予新的对象,例如a=0。
  • 对象离开它的作用域,如func函数执行完毕时,func函数中的局部变量(全局变量不会)。
  • 对象所在的容器被销毁,或从容器中删除对象。
import sys
 
class A():

    def __init__(self):
        pass
 
print("创建对象 0 + 1 =", sys.getrefcount(A()))

a = A()
print("创建对象并赋值 0 + 2 =", sys.getrefcount(a))

b = a
c = a
print("赋给2个变量 2 + 2 =", sys.getrefcount(a))

b = None
print("变量重新赋值 4 - 1 =", sys.getrefcount(a))

del c
print("del对象 3 - 1 =", sys.getrefcount(a))

d = [a, a, a]
print("3次加入列表 2 + 3 =", sys.getrefcount(a))


def func(c):
    print('传入函数 1 + 2 = ', sys.getrefcount(c))
func(A())

创建对象 0 + 1 = 1
创建对象并赋值 0 + 2 = 2
赋给2个变量 2 + 2 = 4
变量重新赋值 4 - 1 = 3
del对象 3 - 1 = 2
3次加入列表 2 + 3 = 5
传入函数 1 + 2 =  3
  • 创建对象 0 + 1 = 1
  • 创建对象并赋值 0 + 2 = 2
  • 赋给2个变量 2 + 2 = 4
  • 变量重新赋值 4 - 1 = 3
  • del对象 3 - 1 = 2
  • 3次加入列表 2 + 3 = 5
  • 传入函数 1 + 2 = 3

引用计数的优缺点

优点

  • 高效、逻辑简单、只需根据规则对计数器做加减法。
  • 实用性。 一旦对象的计数器为零,就说明对象永远不可能再被用到,无须等待特定时机,直接释放内存。
    缺点
  • 需要为对象分配引用计数空间,增大了内存消耗。
  • 当需要释放的对象比较大时,如字典对象,需要对引用的所有对象循环嵌套调用,可能耗时比较长。
  • 循环引用。致命缺陷,无解

标记-清除

标记-清除算法主要用于解决循环引用问题,该算法分为两步:

  • 标记阶段。将所有的对象看成图的节点,根据对象的引用关系构造图结构。从图的根节点遍历所有的对象,所有访问到的对象被打上标记,表明对象是“可达”的。
  • 清除阶段。遍历所有对象,如果发现某个对象没有标记为“可达”,则回收。
class A():
    def __init__(self):
        self.obj = None
 
def func():
    a = A()
    b = A()
    c = A()
    d = A()

    a.obj = b
    b.obj = a
    return [c, d]

e = func()

上面代码中的引用关系如下图:


如果采用引用计数器算法,那么a和b两个对象将无法被回收。而采用标记清除法,从根节点(即e对象)开始遍历,c、d、e三个对象都会被标记为可达,而a和b无法被标记。因此a和b会被回收。
一般而言,根节点的选取包括(但不限于)如下几种:

  • 当前栈帧中的本地变量表中引用的对象,如各个线程被调用的方法堆栈中使用到的参数、 局部变量、 临时变量等。
  • 全局静态变量

分代收集

  1. 接大部分的对象生命周期短,大部分对象都是朝生夕灭。
  2. 经历越多次数的垃圾收集且活下来的对象,说明该对象越不可能是垃圾,应该越少去收集。

Python中,对象一共有3种世代:G0,G1,G2

  1. 对象刚创建时为G0。
  2. 如果在一轮GC扫描中存活下来,则移至G1,处于G1的对象被扫描次数会减少。
  3. 如果再次在扫描中活下来,则进入G2,处于G1的对象被扫描次数将会更少。

整数池

  • Python实现int的时候有个小整数池。为了避免因创建相同的值而重复申请内存空间所带来的效率问题, Python解释器会在启动时创建出小整数池,范围是[-5,256],该范围内的小整数对象是全局解释器范围内被重复使用,永远不会被垃圾回收机制回收。
  • 在pycharm中运行python程序时,pycharm出于对性能的考虑,会扩大小整数池的范围,其他的字符串等不可变类型也都包含在内一便采用相同的方式处理了,这是一种优化机制。
x = 10
y = 10
print(x == y)
True

补充部分

赋值

链式赋值

a = 10
b = 10
c = 10
d = 10
print(f'a:{a}, b:{b}, c:{c}, d:{d}')
a:10, b:10, c:10, d:10
a = b = c = d = 10
print(f'a:{a}, b:{b}, c:{c}, d:{d}')
a:10, b:10, c:10, d:10
print(f'')
#要使用f-string输出变量的值,你可以在花括号内放置变量名,并在f-string前面加上字母"f"。这样,变量的值将会被插入到字符串中。
a = 1
b = 2
c = 3
d = 4

print(f'a: {a}, b: {b}, c: {c}, d: {d}')
#可以将变量的名称和值插入到字符串中,在输出时显示它们的值。请注意,花括号内的变量名称将被替换为对应的值。
a: 1, b: 2, c: 3, d: 4

交叉赋值

x = 100
y = 200

temp = x
x = y
y = temp
print(f'x:{x}')
print(f'y:{y}')

x:200
y:100
x, y = y, x
print(f'x:{x}')
print(f'y:{y}')
x:100
y:200

注释

# 单行注释

'''
三单引号注释
三单引号注释
'''

"""
三双引号多行注释
三双引号多行注释
"""
'\n三双引号多行注释\n三双引号多行注释\n'
  • 加双引号输出文本内容
  • 不加双引号输出变量
#\n 转义符号   放在句末代表话还没说完。
#如果要输出下图这种,要么直接'''   ''' 然后在里面敲回车
#另外一种比较麻烦,就是每一行后面都加一个转义字符\n\



___                     ___          ___          ___     
     /\  \         ___       /\  \        /\__\        /\  \    
    /::\  \       /\  \     /::\  \      /:/  /       /::\  \   
   /:/\:\  \      \:\  \   /:/\ \  \    /:/__/       /:/\:\  \  
  /::\~\:\  \     /::\__\ _\:\~\ \  \  /::\  \ ___  /:/  \:\  \ 
 /:/\:\ \:\__\ __/:/\/__//\ \:\ \ \__\/:/\:\  /\__\/:/__/ \:\__\
 \/__\:\ \/__//\/:/  /   \:\ \:\ \/__/\/__\:\/:/  /\:\  \  \/__/
      \:\__\  \::/__/     \:\ \:\__\       \::/  /  \:\  \      
       \/__/   \:\__\      \:\/:/  /       /:/  /    \:\  \     
                \/__/       \::/  /       /:/  /      \:\__\    
                             \/__/        \/__/        \/__/

posted @   阿K进阶路  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示