[python]常识一


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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值