文章目录
Here we take down the most frequently used python code segments.And as a Javaer,we also keep the notable diffrence,some of which really puzzles me.
记录一些java人觉得新奇的python特性…
py类型标记
py本质是动态类型语言, Python 运行时不强制执行函数和变量类型标记,但这些注解可用于类型检查器、IDE、静态检查器等第三方工具。
有些文档也称之为类型注解,但窃以为谓之 标记更贴切
- 基础数据的 类型标记和 类的类型标记
- 基础容器类型标记
- 容器类型详细标记
- 函数类型标记
Union
# 基础数据类型标记
i: int = 1
j: float = 1.0
class Person(object):
pass
p:Person = Person()
#集合类型标记
my_list: list = [1, 2, 3]
my_tuple: tuple = (1,2,3)
my_set: set = {1,2,3}
my_dict: dict = {'a':1}
print(len(my_dict))
#集合元素类型标记
my_list2: list[int] = [1, 2, 3]
my_tuple2: tuple[int, bool, str] = (1, 'a', True)
# 下面二个语句都能运行
my_dict2: dict[str, int] = {'a': 1}
my_dict3: dict[str, int] = {1: 'a'}
def add(a:int, b:int) -> int:
return a + b
print(add(1,1))
# Union
my_list3: list[Union[str, int]] = [1, 'a']
my_dict4: dict[str, Union[str, int]] = {'a': 1, 'b': 'test'}
def func(params: Union[str, int]) -> Union[str, int]:
pass
py的面向对象特性
- py支持多根继承
- py没有接口(类似java的
interface
) - py 是
duck-typing
动态语言- 一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定
魔术方法
__init__
构造函数__str__
字符串方法,相当于 java 的toString()
__lt__
小于符号比较__le__
小于等于__eq__
等于
class Student(object):
#构造
def __init__(self, name, age):
self._name = name
self._age = age
#toString
def __str__(self) -> str:
return f"Student: username={self._name},age={self._age}"
#compareTo
def __lt__(self, other):
return self._age < other._age
#equals
def __eq__(self, other):
return self._age == other._age
if __name__ == '__main__':
a = Student('ht', 10)
b = Student('ht2', 11)
assert a < b
closure(闭包)
def account(initial_amount=0):
def atm(num, deposit=True):
nonlocal initial_amount # 使用了Nonlocal关键字
if deposit:
initial_amount += num
else:
initial_amount -= num
return initial_amount
return atm
func = account(100)
print(func(100)) # 200
print(func(100, False)) # 100
- 函数嵌套中
- 内部函数使用了外部函数的变量(根本目的)
- 外部函数返回了内部函数,这里的内部函数就是
closure
疑问:closure确实方便,但是使用类封装起来不更好?
variable scope
-
Local variable
-
global variable
-
全局变量只作用于一个py文件 (一个module)
-
可以通过定义全局变量module方式来定义 整个程序的全部变量
-
特殊的函数
globals()
locals()
vars(object)
python的装饰器
装饰器, 和java的装饰模式很类似. 借助装饰器,可以增强原来方法的功能,在audit log (风控), tracing,登录拦截等方面用得很多.
装饰器本质是 closure
如果大家对函数式编程有一点了解, 其实很容易理解这种玩法. 要牢牢记住一个最关键的点:函数可以用变量名去引用它, 可以传递.
def log(func):
def wrapper(*args, **kwargs):
print('someone called ', func.__name__)
return func(*args, **kwargs)
return wrapper
@log
def now():
print('now::: ', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
# 打印出来的字符串是 wrapper
# 加了注解之后 now 指向的 引用 其实是 log(func)返回的 wrapper. (请好好揣摩这句话的含义: 引用指向的函数已经变化了)
print(now.__name__)
# 打印结果是 None
print(now())
python的异常处理
这个和java特别像. 话说相当多的有异常机制的语言都很类似…
try:
print(1/0)
except ZeroDivisionError as e:
print('result-->', e)
raise ValueError('it divided zero')
else:
print('do else..')
finally:
print('done...')
python的模块和包
python的模块(module) 就是 一个个 .py
文件.
python的包(package) 就是一坨.py
组织在了一起形成了一个目录, 其中还有一个__init__.py
特殊文件.
导入包,实质是执行__init__.py,可以在__init__.py中做包的初始化、统一执行的代码、批量导入
另外还有import *
这样导入包内所有模块的子模块的用法,但不推荐
模块搜索路径
导入某个模块之后,解释器从哪里去找到这个模块呢?路径的优先级为:
- 内置模块
- 当前目录
- 程序的主目录
- pythonpath目录
- 标准链接库目录
- 三方库(
site-packages
) .pth
文件内容 (注意不是.path
)sys.path.append()
临时添加的内容
if name == “main”:
对于Java, main
方法是程序入口,但是对于python,没有"入口"也能跑.
当你在一个 run python file
的时候, 解释器其实是从上到下执行的. 那好奇一下,if __name__ == "__main__":
干啥用的呢?
假如有2个文件(模块),一个是py_test.py
def combine(a, b):
return a + b
if __name__ == '__main__':
print(combine(1,1))
# 如果 只 run pytest.py,则这里输出 __main__
# 如果从外面的某个模块 import py_test 该模块之后,则这里输出 py_test (该模块名字)
print(__name__)
另一个是test_1.py
:
import py_test
print(py_test.combine(1,2))
# 输出 __main__
print(__name__)
我们比较下 两个模块中的输出可得知:当__name__
在当前模块中就是 __main__
, 在引入方模块中就是模块名.
那这有啥用呢? python是解释语言,当一个模块A被导入另一个模块时, 其实模块A 会被从上–>下 执行一遍 . 有不少人习惯在 模块(文件)最后写一些测试代码,但是不希望在引用方模块中被执行. 所以就搞了这么个if __name__ == '__main__':
.
简单粗暴理解,它就是 模块的入口.
作为一个java人,这真的好生有趣…
模块的加载
导入一个模块时,模块中的代码都会被执行。但当再次导入这个模块,则不会再次执行。
祖师爷之所以这样设计,是因为导入模块时 需要导入函数、变量等,不需要反复定义和执行。
(有点像java的类初始化)
函数定义中的箭头
这种语法在python中叫做 type hints ,比如下面的例子:表示传参时一个 str, 返回是一个 int, 但这并不是 强制的…也就是说,即使不遵守这种约定,也是 work的. 习惯了 java 等静态语言的人,可能还真的不大适应…
这种语法也可以被视为 python 中注释的一部分
def useful_func(x: str) -> int:
return int(x)
# it works
print(useful_func('11'))
# it also works
print(useful_func(123))
@staticmethod
标识类的方法是 静态的,不需要创建个对象就可以使用,这个跟Java如出一辙
class Person(object):
@staticmethod
def talk():
pass
if __name__ == '__main__':
Person.talk()
@Property 和 setter
一直不太理解 下面例子中的@fullname.setter
是个啥意思…直到幡然醒悟这个例子中的注释…
class Person():
def __init__(self, first_name, last_name):
self.first = first_name
self.last = last_name
# @property 将把 fullname方法几乎 转换成了 一个属性
@property
def fullname(self):
return self.first + ' ' + self.last
# 这里的setter意思是: 当对 fullname这个属性(本来是方法)有赋值时,就触发改方法.
# 注意::: 实际是对后面的 person.fullname = 'li si' 起作用了
@fullname.setter
def fullname(self, name):
first_name, last_name = name.split()
self.first = first_name
self.last = last_name
person = Person('zhang', 'san')
print(person.fullname,'--', person.last,'--', person.first)
person.fullname = 'li si'
print(person.fullname,'--', person.last,'--', person.first)
@Property
@Property
对java来说是个新奇的玩意. 看上去极其像是java的注解,但其实完全是两码事.
下面给出了2个例子,刚好说明了 @Property
的两大用途:
- 其一:让对象可以像属性一样使用方法
- 其二: 隐藏对象的内部属性,即
@property
会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。
class Student(object):
def __init__(self, name, age):
self._name = name
self._age = age
# @property的作用一: 让对象可以像使用属性一样使用方法
@property
def get_name(self):
return self._name
s1 = Student("ht", 19)
print(s1._name)
# print(s1.__age)
# TypeError: 'str' object is not callable
# print(s1.get_name())
print(s1.get_name)
class Person(object):
def __init__(self, name, age):
self.__name = name
# @property的第二个用途: 将属性(比如这里的 _name )隐藏起来 (连getter 有没有),直接使用 x.name 调用
@property
def name(self):
return self.__name
p = Person('haha', 10)
print(p.name)
Pickle/json
- Pickle 是 Python 自己的 二进制协议格式, JSON是通用的文本协议
- 默认情况下,JSON 只能表示 Python 内置类型的子集,不能表示自定义的类;但 pickle 可以表示大量的 Python 数据类型
简单说: Pickle 就像是java里的ObjectOutputStream
json 操作
import json
s="['foo', {'bar': ('baz', None, 1.0, 2)}]"
# 把对象搞成Json str,这里的例子其实是把 s 这个str 对象,搞成了json str
# 序列化
j=json.dumps(s)
print(j)
s2= '''{
"a": 1,
"b": "啊"
}'''
# 反序列化
o=json.loads(s2)
print(o)
with语句
with 语句支持通过Context Manager
定义运行时上下文 , 在语句体被执行前进入该上下文,并在语句执行完毕时退出该上下文,即:
contextmanager.__enter__()
contextmanager.__exit__()
Python 定义了一些Context Manager
来支持简易的线程同步、文件或其他对象的快速关闭等.
这个特性就跟java 的 try ()
(jdk7+) 的语法类似.
id()
l=[1,2]
print(id(l))
l.append(3)
# append() 前后 id() 值不变, 也就是说 是同一个对象. id() 可以视为判断是不是同一个对象的方法
# This is guaranteed to be unique among simultaneously existing objects.
# (CPython uses the object's memory address
print(id(l))
tuple
t=tuple((1,2))
t=(1,2)
# 实际上这里的 , 才决定了 这个变量是个tuple; 而不是 上面的括号
t = 1,2
# True
assert type(t) == tuple
逻辑判断
python的任意对象都可以进行逻辑值检测;
一个对象默认被视为真值,除非当该对象被调用时其所属类定义了__bool__()
方法且返回 False 或是定义了 __len__()
方法且返回零.
(坦白说,对于习惯使用静态的java 的人来说, 略别扭)
# False
# assert []
# False
# assert ''
class Test:
def __len__(self):
return 0
t=Test()
# true
assert not t
设计模式
单例
py的单例挺繁琐,必须要另行创建一个 .py
产生单例对象
#1) singleton.py
class Singleton:
pass
singleObj=Singleton()
#2) test.py
import singleton
s1=singleton.singleObj
s2=singleton.singleObj
print(s1 is s2) #true
多线程&并发
这个话题太大了,后面另作专题;这里简单的写个demo
def foo(msg):
while True:
print('--->', msg)
time.sleep(1)
def bar(text):
while True:
print('+++>', text)
time.sleep(1)
if __name__ == '__main__':
import threading
threading.Thread(group=None, target=foo, args=('ha',), name="my_thread_1").start()
threading.Thread(group=None, target=bar, args=('da',), name="my_thread_2").start()