【Java 转 Python】Python 速通(一)
Java背景,前面刚速通了Golang,新任务AI相关上Python了。在此记录一下Python里比较特别的语法或者关键字。因为大多数的概念都是相通的,记住不一样的syntax就能写个七七八八。虽然基本也都是vibe coding,读懂代码还是很有必要的。
·
Java背景,前面刚速通了Golang,新任务AI相关上Python了。在此记录一下Python里比较特别的语法或者关键字。因为大多数的概念都是相通的,记住不一样的syntax就能写个七七八八。虽然基本也都是vibe coding,读懂代码还是很有必要的。
语法
- pass 空占位,没有意思,防止脚本解释运行时不出错的占位符
- 变量不分基本类型和引用类型,一切变量都是对象。它们的内存方式确实都是内存空间+引用地址。但是相对于Java的基本类型和引用类型区分,Python是用immutable/mutable可变型进行区分的。int, float, bool, str, tuple, None是不可变类型,list, dict, set 以及类实例是可变类型
只有当你需要控制对象创建的过程时,才需要用到 new。 - print f"abc {val}" 字符串内容由变量替换,如果后面的字符串有变量,前面用f提示
- raise 主动抛出异常,和Java 里的throw差不多,不过Python里不需要像Java一样需要关键字new。
- try…except 捕获异常,相当于Java里的try-catch。如果except后面没有具体的类,直接是except: 默认是Exception类的实力
- (DataModel), @dataclass注解
- with open(…) as … 读写文件的语法,可以自动关闭文件并释放资源,无论代码在读取时是否发生异常。比较像Java中的try-with-resources语句
- 字符串 单引号’ '或双引号" " 定义的,单行字符串
- docstring “”" “”" 多行注释,描述函数功能,也可以是多行字符串
- def 定义函数关键字,函数的参数是可以有默认值的,如果定义了默认值,函数调用的时候可以不传实参。一般有默认值的函数形参在定义的时候靠右列举。不建议默认值为空的列表或空的字典,因为这俩是引用类型,如果函数体中对这俩进行了修改,这个修改会一直跟着引用走。例如
def say(name, friends=[])
friends.append(name)
print(friends)
say('zhangsan') # 打印张三
say('lisi') #打印张三,李四
- None 空值,可作为默认值,如果是默认值,方法里一般会判断 if val is None
- x[:] 切片语法,表示x 这个列表复制了一份,浅拷贝,拷贝了一层皮儿
- *args 表示把传进来的实参打包成tuple, **args表示把传过来的实参打包成dict
- 模块:把一堆函数代码放到一个.py文件中
- import 导入模块,或者from module import xxx导入具体定义的类、方法等
- __ name __ 表示模块名,如果是程序入口,会被替换为__main__
- ** (双星号运算符): 字典解包(Unpacking)运算符。它会将字典中的所有键值对“拆解”出来。{**dict1, **dict2}: 这种语法会创建一个新的字典,并把 dict1 和 dict2 的内容都放进去。如果两个字典中有相同的键,后者(dict2)的值会覆盖前者
OOP
- init 类的初始化方法。这个不是constructor构造方法。在 Python 中,对象的创建过程实际上是由两个方法协作完成的:new 和 init。前者负责开辟内存空间,后者负责对开批号的对象空间进行初始化复制。__init__方法的第一个参数是self,这里其实就是说明当前方法被调用的时候,对象其实已经存在了。一般在Python代码中,初始化方法见得多,当init 被调用的时候,传进来的self都是新的对象,也就是非单例模式,此时new 方法已经被调用过了,默认调用的是object的new方法。如果要执行单例模式,则要重写new方法。注意,如果 new 没有返回当前类的实例,那么 init 将永远不会被调用。另外,init方法不是必须的,如果不写那么默认继承父类的init方法进行初始化。一般只有类中没有成员属性,只有成员方法和静态(不属于对象)的属性时,不写init 方法。
- 类的实例化:student = Student(a,b,c,d),用类名实例化,实际上是调用了Student的初始化方法
- __age 以下划线开头的属性名称为私有属性,但是这个私有属性居然不是被严格限制的,只是一个种大家都遵循的协议……
- @property 装饰器,可以把私有属性装饰的像公共属性一样可以用.来读。这里我还查了一下,Python的decorator 装饰器和Java 的annotation 注解是不是以一个东西,居然不是。Java 的注解偏向标签性质,仿佛一个接口。但是Python装饰器会自动把被装饰的函数直接改写运行被修饰的方法,感觉像语法糖。Python可以有方法名相同,但装饰器不同的方法,是因为在解释运行的时候会被改写成不同的方法。以上说的是方法装饰器,Python还有类装饰器。
- class B(A) 表示类的继承,B继承A类。B类对A进行扩展的时候,初始化函数中推荐先调用super().__init__再对子类进行初始化。Java 过来的话习惯上是这样的,但是这个顺序不是严格的,理论上可以交换,还是因为init 它是一个初始化函数,而不是构造函数,实际上对象已经分配出来了,你可以后调用父类的初始化函数进行复制,但是这样比较有风险,可能出现父类值复制覆盖了子类。
- 类属性、类方法,即属性和方法是属于类的,而不是属于实例化的对象的。就像Java 中的静态方法静态变量。在Python里声明在__init__方法前的是类属性,类方法的话加上@classmethod的方法是类方法,类方法的话默认形参为cls而不是self用以区分。访问的时候直接通过类名/对象访问。
- @staticmethod 静态方法和类方法的区别就是,静态方法逻辑完全独立,参数既不需要 self 也不需要 cls,仅仅是为了代码归类方便
- 反射相关方法:setattr, getattr, hasattr, isinstance
- call 当对象当做函数调用的时候,这个方法被触发。Python有一个Java 里我认为不是很常见的东西,就是函数作为参数传递给方法,进而把函数作为对象属性。这里如果把一个函数传递给一个对象作为fn属性,然后在__call__方法中对fn增加前置和后置逻辑,就完成了一次函数的扩展。这里很像Java 的AOP,成果上都是对一个方法进行了扩展。但是实现方式两个语言是不太一样的。
- magic method,魔术方法,以__开始和结尾为名称的一些对象方法,这些方法的共同特点就是“拦截”,在已有方法被触发的时候,并不是直接执行原来的方法,而是被拦截执行了魔术方法,但是魔术方法里 可以对原来方法进行了增强。
- def build(self, session_id: str) -> Agent 理解常见的方法定义:self 显式声明了这个方法和对象实例的绑定关系。但调用的时候不需要传self对象
异步、并发、协成、多线程
- async def 表示这是一个异步函数。当调用这个方法时,它不会立即执行内部代码,而是返回一个 协程对象 (Coroutine Object)。必须使用 await 关键字或者将其交给事件循环(Event Loop)来驱动它运行。async 这个关键字本身代表的异步含义和Java 是不一样的,Java 里的异步是多线程。而这里的异步是开了一个coroutine(不是goroutine),async 的真实作用是:允许这个函数在内部使用 await。
- yield 关键字,当函数执行到yield时,返回一个值给调用者,并同时暂停执行,保存当前所有状态。当调用者请求下一个值时,函数继续。同时也是coroutine的实现基础
其他
- 描述符类,主要的作用是解耦,把类的属性和对于属性的监控方法单独封装成一个类,这个类叫描述符类,它实例化的对象就是描述符对象。
- 关于命名:模块,用一个py文件为范围的,是小写字母,用下划线分割命名,比如good_car.py,类是驼峰命名,和Java一样。除了类,其他都是小写字母下划线分割命名。
- json.loads() 解析JSON格式的字符串,方法调用时字符串会转换成字典。这里面的s代表的是string。如果是json.load表示解析.json文件。总的来说不带s的是文件模式,带s的是字符串模式。 json.dumps()/dump()相应地是把字典 数据转为结构化JSON字符串。
- 项目里的__init__.py 的核心作用是告诉 Python 解释器:“请把这个目录当成一个 Python 包(Package)来对待,而不仅仅是一个普通的文件夹。文件本身可能是空的,但是不建议省略。
- Python 中.py文件是一个模块,带__init__.py的表示它的上级目录是一个包。package和module就是区分项目的组织层级的。本质是一种分类和索引的功能
- @alru_cache(maxsize=128, ttl=3600) 装饰器,装饰在方法上,表示方法的返回结果会放在缓存中。alru代表启动异步LRU缓存,这个缓存是本地缓存,在Python 进程的堆内存(Heap Memory)中,即内存的一部分。它的大小是128个返回对象,过期时间是3600s。对于过期的清理,是由最后的读写协成顺手处理的,没有专门的清理协程
代码片段理解
return await asyncio.to_thread(self.some_builder.build, some_args)这里不是方法的立即调用,而是把build方法的引用,和后面some_args(即build方法需要的参数)一起注册在一个单独的线程(to_thread)上。通常是这个build方法有比较耗时的IO操作且是同步的,Python 支持把这样的操作用asyncio.to_thread方式异步放在一个单独线程中。await表示在线程运行期间,不占用CPU资源,而当运行完成之后的结果会被await接收到,并用return返回给调用方。这里为什么给to_thread传递了方法引用,而不是直接调用方法?是Python在这里执行了委托执行模式。build 方法是在 asyncio.to_thread 内部创建的新线程启动后被触发的class SomeType(str, Enum)增强版枚举类的标准写法,类定义括号里的逗号表示这个类同时继承了多个父类。当前类同时有枚举属性(可以遍历),同时也是个字符串。
讲道理,连续学了Go和Python 并且工作中开始写Python之后,发现Java真的是白月光,虽然很多人唱衰,但是我觉得Java 的代码是最漂亮的,可以写的非常优雅,逻辑严谨,泾渭分明,我真的非常喜欢。脚本语言总给我很乱的感觉。作为一个转码选手,我觉得上来写Java 是我的幸运,Java 庞大的生态、Spring 框架,和JVM, JMM那些理解,真的是写了别的代码,才知道八股文不白背,基础有它的分量在。
更多推荐


所有评论(0)