作者归档:runyu

跨域请求

跨域请求(Cross-Origin Resource Sharing, CORS)是一种机制,它允许网页上的脚本能够向另一个与其原始来源不同的域请求资源。这种机制是由浏览器的同源策略(Same-Origin Policy)所约束的,该策略默认情况下禁止网页请求非同源的服务器资源,以保护用户免受恶意网站的攻击。

同源策略

同源策略是Web安全的基石之一,它限制了一个源中加载的文档或脚本如何与另一个源的资源进行交互。这有助于防止恶意网站读取另一个网站上的敏感数据。两个URL的协议、域名和端口号都必须相同,才被认为是同源的。

跨域请求的工作原理

当你尝试从一个源的网页上通过JavaScript发起对另一个源(域名、协议或端口号不同)的资源请求时,浏览器会自动应用同源策略,阻止这种跨域请求。为了使这些请求成功,目标资源的服务器必须通过发送适当的CORS头部来明确允许请求来源的域访问该资源。

例如,如果一个来自https://example.com的网页试图请求https://api.example.org/data的资源,那么api.example.org服务器必须在响应中包含一个Access-Control-Allow-Origin头部,指定https://example.com(或*代表任意域)可以访问该资源。

CORS请求类型

  • 简单请求(Simple Requests):使用GET、HEAD或POST方法(POST请求的Content-Type只能是application/x-www-form-urlencodedmultipart/form-datatext/plain)的请求。这些请求不会先发送预检请求。
  • 预检请求(Preflight Requests):对于那些可能对服务器数据产生副作用的HTTP请求方法(如PUT、DELETE或含有除简单请求之外的头部的POST请求),浏览器会先发送一个OPTIONS请求,询问目标资源是否愿意接受来自源应用程序的请求。

解决跨域问题

  • CORS响应头:服务器可以通过设置CORS响应头(如Access-Control-Allow-Origin)允许特定的外部域访问其资源。
  • JSONP(已过时):通过动态添加<script>标签的方式绕过同源策略限制,仅限于GET请求。
  • 代理服务器:通过在同源策略限制下的服务器上设置代理服务器转发请求到目标服务器,从而绕过浏览器的同源策略限制。

跨域请求机制是现代Web开发中非常重要的一部分,特别是在构建单页面应用(SPA)和使用各种Web API时。正确理解和应用CORS可以帮助开发者安全地实现跨域资源共享。

git note

变基的风险

呃,奇妙的变基也并非完美无缺,要用它得遵守一条准则:

如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。

如果你遵循这条金科玉律,就不会出差错。 否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。

变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。 如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase 命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。


git stash
是 Git 版本控制系统中的一个命令,用于临时保存工作目录和暂存区的改动,让你能够拥有一个干净的工作状态。这个命令在你需要切换分支处理其他任务,但当前分支的工作尚未完成到一个可以提交的状态时非常有用。使用 git stash 时,你的修改会被保存在一个未完成改动的栈中,可以在之后任何时候重新应用。

Fast-forward mergeSquash merge 都是Git中合并分支的方法,但它们在处理分支历史方面有显著的不同。

Fast-forward Merge

  • 当你的当前分支完全落后于要合并的分支时,Git可以执行快进合并(fast-forward merge)。这意味着没有在当前分支上而在其他分支上进行的提交,所以Git仅仅是将HEAD指针移到最新提交上,实际上就是“追上”了变更历史。
  • 快进合并不会创建一个新的合并提交,这使得历史记录看起来非常线性。
  • 这种类型的合并保留了项目历史的实际发展路径,但如果你想保留特定分支的历史记录,这可能不是最佳选择。

Squash Merge

  • 在执行Squash合并时,Git会将所有从分支开始以来的提交压缩成一个新的提交到目标分支上。这意味着不管有多少个提交,最终只会在目标分支上创建一个新的提交。
  • Squash合并使得历史记录更加整洁,因为它将所有的变更压缩到一个提交中,这对于保持一个干净的主分支(如master或main)历史记录非常有用。
  • 使用squash合并,原始分支的详细提交信息可能会丢失,因为所有的变更都被压缩到一个提交中。
  • Squash合并不会自动删除源分支,因此在合并后需要手动删除分支来保持分支清晰。

使用场景

  • Fast-forward Merge 更适合小规模的、频繁的更新,尤其是当你希望保留详细的分支合并历史时。
  • Squash Merge 适合在将特性分支合并回长期分支(如main或master)之前,保持历史记录的简洁。它特别适用于大型特性开发,其中包含了许多中间提交,这些提交的历史记录对于最终的代码库可能不是很重要。

选择哪种合并策略取决于你的项目需求以及你想如何管理你的Git历史记录。

English

Thus far he wasn’t boasting about results.

but rest assured Nuitka stands out from the rest!

underlying principles 基本原则

but be aware that you may hit roadblocks along the way and will need to be comfortable debugging them.

utilities utility utils

Python

pyside6-deploy

This tool is intended to help in-experienced developers with freezing and compiling tools to get an executable from their PySide project. We include a simple wrapper for Nuitka, so you can just run pyside6-deploy your_file.py to get it done. 

此工具旨在帮助经验丰富的开发人员使用冻结和编译工具,从PySide项目中获取可执行文件。我们为Nuitka提供了一个简单的包装器,因此您只需运行pyside6-deploy your_file.py来完成它。

类属性和实例属性

class test:
    a = 1;
    def __init__(self):
        print(self.a)
        self.a = 2
        print(self.a)
        print(test.a)
        test.a = 2
        print(test.a)
        
new = test()
print(new.a)
输出结果:
1
2
1
2

程序员注意事项: 在类定义内定义的变量是类属性;它们将被类实例所共享。 实例属性可通过 self.name = value 在方法中设定。 类和实例属性均可通过 “self.name” 表示法来访问,当通过此方式访问时实例属性会隐藏同名的类属性。 类属性可被用作实例属性的默认值,但在此场景下使用可变值可能导致未预期的结果。 可以使用 描述器 来创建具有不同实现细节的实例变量。来源

全局解释器锁(GIL)

全局解释器锁(GIL)是 Python 解释器的一个重要组成部分,它是一个互斥锁,用于控制在任何时刻只有一个线程可以执行 Python 解释器中的字节码。这个锁可以确保在多线程环境下,Python 程序能够正确地执行,避免了并发访问共享数据时的竞态条件和数据不一致性问题。

GIL 的作用是在解释器级别上限制了线程的并发性,因为在任何时刻只能有一个线程持有 GIL,获得 GIL 的线程可以执行 Python 字节码,而其他线程则被挂起。这样做可以避免多个线程同时访问共享数据时的竞态条件和数据不一致性问题,因为只有一个线程可以访问共享数据,而其他线程必须等待。

GIL 的存在在某种程度上限制了 Python 的多线程并发性能,因为在任何时刻只有一个线程可以执行 Python 代码,而其他线程被挂起。如果程序中的多线程任务是 CPU 密集型的,那么这个限制可能会对程序性能产生重大影响。然而,在 I/O 密集型任务中,由于 I/O 操作通常会导致线程挂起,因此 GIL 的限制通常不会成为程序性能的瓶颈。

需要注意的是,GIL 是特定于 CPython 解释器的实现细节,其他 Python 实现(如 Jython、IronPython 和 PyPy)可能没有 GIL 或使用不同的锁实现。同时,对于需要高度并发性的应用程序,可以使用其他并发编程技术(如多进程、协程等)来避免 GIL 限制。

Python 的协程

协程(用户态线程)

由于线程分用户线程和内核线程。内核线程再调用的时候可以去不同的核心去操作。所以多线程是可以利用到多核的。

Python 的协程(coroutine)是一种轻量级的并发编程技术,可以在单个线程中实现并发执行多个任务的效果,从而提高程序的性能和响应能力。下面是一个简单的协程示例:

import asyncio

async def greet(name):
    print(f"Hello, {name}!")
    await asyncio.sleep(1)
    print(f"Goodbye, {name}!")

async def main():
    await asyncio.gather(
        greet("Alice"),
        greet("Bob"),
        greet("Charlie")
    )

asyncio.run(main())

在这个例子中,greet 函数是一个协程,它使用 async def 声明,这表示它是一个异步函数。该函数包含了两个打印语句和一个 await asyncio.sleep(1) 语句。await 表示当前的协程需要等待一个异步操作完成,而这里的异步操作是 asyncio.sleep(1),它会让协程挂起一秒钟,然后恢复执行。

main 函数中,使用 asyncio.gather 函数来并发执行三个 greet 协程。这个函数会等待所有协程执行完毕,然后返回它们的结果。

运行这个示例后,我们会看到类似如下的输出:

Hello, Bob!nHello, Charlie!nGoodbye, Alice!nGoodbye, Bob!nGoodbye, Charlie!

可以看到,在这个示例中,三个 greet 协程被并发执行,每个协程执行到 await asyncio.sleep(1) 时会挂起,然后切换到其他协程继续执行。这样可以实现在单个线程中同时执行多个任务的效果,从而提高程序的性能和响应能力。

需要注意的是,在使用协程时需要注意避免阻塞操作,因为协程是在单个线程中并发执行的,如果一个协程阻塞了,那么其他协程也会被阻塞。通常可以使用异步 I/O 或者线程池等技术来避免阻塞操作,从而提高程序的性能和并发能力。

Python协程(coroutine)是一种更高级的控制流结构,可以看作是生成器的扩展。与生成器类似,协程也是通过关键字 yield 来暂停和恢复执行的,但协程可以在暂停的地方重新启动执行,甚至可以从外部发送数据进入协程。要理解Python协程的底层原理,以下几个关键点是必须掌握的:

  1. 生成器
    • 生成器是迭代器的一种特殊形式,通过 yield 关键字生成值。
    • 调用 yield 的时候,生成器会暂停执行,并将值返回给调用方。
    • 当生成器函数再次被调用时,会从上次 yield 的位置继续执行。
  2. 生成器对象
    • 当一个生成器函数被调用时,它返回一个生成器对象,而不是立即开始执行函数体。
    • 生成器对象实现了迭代器协议 (__iter____next__ 方法)。
  3. 协程基础
    • 协程也是通过 yield 实现的,但更进一步的是,协程允许通过 yield 向其内部发送值。
    • 协程通过 yield 表达式接收外部发送的数据,这使得协程不仅能生成值,还能接收值并控制执行流程。
  4. yield 表达式
    • yield 可以作为表达式来使用,例如 value = (yield some_value),这样 yield 不仅返回 some_value,而且可以通过 send 方法接收外部传入的 value
  5. 协程的状态控制
    • 协程的状态由其生成器对象的生命周期管理。当调用 next()send() 方法时,协程会从上次暂停的地方继续执行。
    • 当协程运行结束时,会引发 StopIteration 异常。
  6. asyncawait
    • async def 声明一个协程函数,await 关键字用于暂停协程的执行,等待一个耗时操作完成。
    • 底层仍然是基于生成器的工作机制,但有更好的语法支持和语义约束。

下面是一个简单的例子来演示协程的工作机制:

python

复制代码

def simple_coroutine(): print('Coroutine started') x = yield print('Received:', x) # 创建生成器对象 coro = simple_coroutine() # 启动协程 next(coro) # 输出: Coroutine started # 向协程发送数据 coro.send(42) # 输出: Received: 42

在这个例子中,simple_coroutine 是一个简单的协程,使用 yield 来暂停和恢复执行。在 coro.send(42) 调用之后,值 42 被发送到协程内部,并赋值给变量 x

底层实现原理

Python的协程底层实现主要依赖于生成器的状态管理机制。每次调用 yield 时,生成器对象的状态会保存到生成器帧中。生成器帧包含当前执行的位置(指令计数器)、局部变量和堆栈状态。通过调用 next()send() 方法,生成器帧会被恢复,并继续执行直到遇到下一个 yield 或函数返回。

在Cpython中,协程的执行由_PyGen_Send函数控制,该函数负责恢复生成器帧的执行,并处理yield表达式的值传递。协程的高级特性如asyncawait则通过事件循环(如asyncio)进行管理,提供异步I/O操作的支持。

总结起来,Python协程的底层原理主要基于生成器的状态管理机制,通过yield实现暂停和恢复执行,并通过事件循环实现异步操作。这使得协程在处理异步编程任务时非常高效和灵活。

生成器

Python的生成器是一种特殊的迭代器,可以用来生成一系列的值,而不需要一次性将所有值存储在内存中。生成器可以通过 yield 关键字来定义,在函数执行时,当遇到 yield 语句时,会将当前函数的状态保存下来,并返回一个生成器对象。每次从生成器中调用 __next__() 方法时,函数会从上次暂停的地方继续执行,直到遇到下一个 yield 语句或函数结束。

生成器的好处在于,它可以节省内存空间,并且在处理大量数据时,可以实现按需生成数据,提高程序的效率。下面是一个简单的示例代码,演示了如何使用生成器来生成斐波那契数列:

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
for i in range(10):
    print(next(fib))

上面的代码定义了一个名为 fibonacci() 的生成器函数,使用 yield 语句生成斐波那契数列。在主程序中,创建了一个生成器对象 fib,并通过 next() 方法调用了10次,每次调用都会输出一个斐波那契数列的元素。

生成器是一种非常强大的工具,可以用来处理大量的数据,提高程序的效率,也可以用来实现协程,完成异步编程等高级应用。

在生成器函数中,通常会使用 while True 循环来不断生成数据。这是因为生成器是一种惰性求值的迭代器,它只在需要时才会生成数据,并且可以不限次数地生成数据。因此,当我们在使用生成器时,通常会通过 next() 方法不断从生成器中取出数据,直到生成器结束。

在生成器函数中使用 while True 循环,可以保证生成器可以不限次数地生成数据,直到程序主动终止或者生成器函数内部返回一个 StopIteration 异常来结束生成器。在 while True 循环中,通常还会使用 yield 语句来返回生成器的值,因此每次调用 next() 方法时,都会从上次暂停的位置继续执行生成器函数,并生成下一个值,直到结束为止。

惰性求值(Lazy evaluation)是一种编程语言的求值策略,它的特点是在程序需要使用数据时才进行求值。与之相对的是及早求值(Eager evaluation),它的特点是在程序执行时尽可能早地对表达式进行求值。

在惰性求值的策略中,表达式的求值被推迟到程序需要使用它的结果时。这种策略的优势在于可以避免一些不必要的计算和存储,从而提高程序的效率和性能。另外,惰性求值还可以让程序具有更好的组合性和灵活性,因为程序可以按需组合各种操作,并且不必事先计算所有的结果。

在 Python 中,生成器是一种惰性求值的迭代器,它只在需要时才会生成数据,并且可以不限次数地生成数据。通过使用生成器,我们可以避免一次性生成大量的数据导致内存溢出等问题,同时也可以使程序具有更好的组合性和灵活性。除了生成器,Python 中还有一些惰性求值的工具和函数,例如 map()filter()zip() 等。

map()filter()zip()

这三个函数是 Python 内置的用于操作可迭代对象的函数,它们都属于惰性求值的策略。

  1. map()
numbers = [1, 2, 3, 4, 5]
squares = map(lambda x: x**2, numbers)
print(list(squares))  # [1, 4, 9, 16, 25]

map() 函数接受一个函数和一个可迭代对象,它会对可迭代对象中的每个元素应用函数,并返回一个新的可迭代对象。例如,我们可以使用 map() 函数对列表中的所有元素求平方:

  1. filter()

filter() 函数接受一个函数和一个可迭代对象,它会对可迭代对象中的每个元素应用函数,返回所有使函数返回 True 的元素组成的新的可迭代对象。例如,我们可以使用 filter() 函数过滤掉列表中的奇数:

numbers = [1, 2, 3, 4, 5]
even_numbers = filter(lambda x: x % 2 == 0, numbers)
print(list(even_numbers))  # [2, 4]
  1. zip()

zip() 函数接受多个可迭代对象,它会返回一个新的可迭代对象,其中每个元素是输入的可迭代对象中对应位置的元素组成的元组。例如,我们可以使用 zip() 函数将两个列表按位置组合:

names = ["Alice", "Bob", "Charlie"]
ages = [20, 30, 40]
data = zip(names, ages)
print(list(data))  # [("Alice", 20), ("Bob", 30), ("Charlie", 40)]

这三个函数都是非常常用的 Python 内置函数,它们可以让我们在不显式遍历可迭代对象的情况下,对其进行操作和处理,从而提高程序的效率和可读性。同时,它们也支持惰性求值的特性,可以处理非常大的数据集而不会造成内存溢出等问题。

1、*args保存多余变量,保存方式为元组。
2、**args保存带有变量名的多余变量,保存方式为字典。

迭代器在访问元素时不会复制可迭代对象,它只是在内部维护一个指针(或索引),用以记录当前访问的位置。

在 Python 中,要构造一个可迭代对象,需要实现 __iter__() 方法。可迭代对象可以是任何实现了 __iter__() 方法并返回一个迭代器的对象。

迭代器对象还需要实现 __next__() 方法,以便可以逐一访问元素。

class MyRange:
    def __init__(self, start, end):
        self.start = start
        self.end = end

    def __iter__(self):
        return MyRangeIterator(self.start, self.end)

class MyRangeIterator:
    def __init__(self, start, end):
        self.current = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.end:
            raise StopIteration
        else:
            current = self.current
            self.current += 1
            return current

# 使用自定义的可迭代对象
my_range = MyRange(1, 5)

for num in my_range:
    print(num)  # 输出: 1 2 3 4

生成器是一种特殊的迭代器ーー优雅的迭代器。

推导式 Comprehension

Comprehension是一种简洁的语法结构,用于生成新的序列(如列表、集合、字典等)。Python支持四种主要的comprehension:列表推导式(List Comprehension)、集合推导式(Set Comprehension)、字典推导式(Dictionary Comprehension)和生成器表达式(Generator Expression)。

[表达式 for 变量 in 可迭代对象 if 条件]

字符串方法

内置类型 — Python 3.12.4 文档

Fredrik Lundh 曾经建议以下一组规则来重构 lambda 的使用:

  1. 写一个 lambda 函数。
  2. 写一句注释来说明这个 lambda 究竟干了什么。
  3. 研究一会这个注释,然后想出一个抓住注释本质的名字。
  4. 用这个名字,把这个 lambda 改写成 def 语句。
  5. 把注释去掉。

“超类”是指一个类的父类或基类。一个类可以从一个或多个超类继承属性和方法。继承是面向对象编程中的一个重要概念,它允许你创建一个新类,该类可以重用一个或多个已存在类的属性和方法。

子类化是指创建一个新的类,该类从一个或多个已存在的类中继承属性和方法。新创建的类称为子类,而被继承的类称为父类或超类。子类可以继承父类的特性,并且可以通过添加新的属性和方法来扩展或修改其行为。

Python 切片语法是一种便捷的方法,用于从序列(如列表、元组、字符串等)中提取部分元素。Python 的切片操作非常灵活,基本语法如下:

arduino

Copy code

序列[start:stop:step]

这里,startstopstep 分别表示切片的起始位置、终止位置和步长。下面是具体的介绍:

  1. start: 切片的开始索引,序列从这里开始提取。如果不指定,默认从序列的开始处提取。
  2. stop: 切片的结束索引,序列在这里结束提取,但不包括此索引指向的元素。如果不指定,默认提取到序列的末尾。
  3. step: 步长决定了从startstop间提取元素的间隔。如果不指定,默认步长为1,即连续提取。步长可以是负数,这时会逆向提取元素。

示例

  • 基本切片:pythonCopy codelst = [0, 1, 2, 3, 4, 5] print(lst[1:4]) # 输出: [1, 2, 3]
  • 省略开始或结束索引:pythonCopy codeprint(lst[:3]) # 输出: [0, 1, 2] print(lst[3:]) # 输出: [3, 4, 5]
  • 负数索引:pythonCopy codeprint(lst[-4:-1]) # 输出: [2, 3, 4]
  • 步长使用:pythonCopy codeprint(lst[0:5:2]) # 输出: [0, 2, 4] print(lst[::-1]) # 输出: [5, 4, 3, 2, 1, 0], 逆序列表

注意点

  • 如果startstop指定的索引超出了序列的范围,Python 会自动将其调整到序列的边界。
  • 切片操作返回一个新的序列,不影响原始序列。

切片是处理数据时非常有用的工具,能够简洁地表达复杂的序列操作。在数据处理、机器学习等领域中非常常见。

method — 方法

在类内部定义的函数。如果作为该类的实例的一个属性来调用,方法将会获取实例对象作为其第一个 argument (通常命名为 self)。参见 function 和 nested scope

descriptor — 描述器

任何定义了 __get__()__set__() 或 __delete__() 方法的对象。 当一个类属性为描述器时,它的特殊绑定行为就会在属性查找时被触发。 通常情况下,使用 a.b 来获取、设置或删除一个属性时会在 a 类的字典中查找名称为 b 的对象,但如果 b 是一个描述器,则会调用对应的描述器方法。 理解描述器的概念是更深层次理解 Python 的关键,因为这是许多重要特性的基础,包括函数、方法、特征属性、类方法、静态方法以及对超类的引用等等。

在 Python 中,is not!= 是两个不同的运算符,它们用于不同的比较场景。以下是它们的区别:

1. !=(不等于运算符)

  • 功能!= 用于比较两个对象的值是否不相等。
  • 适用场景:当你想要比较两个对象的内容或值是否不同时,使用 !=
  • 示例
  a = [1, 2, 3]
  b = [1, 2, 3]
  print(a != b)  # False,因为 a 和 b 的值相等

2. is not(身份运算符)

  • 功能is not 用于比较两个对象的身份(即内存地址)是否不同。
  • 适用场景:当你想要判断两个对象是否不是同一个对象(即它们是否指向不同的内存地址)时,使用 is not
  • 示例
  a = [1, 2, 3]
  b = [1, 2, 3]
  print(a is not b)  # True,因为 a 和 b 是不同的对象,即使它们的值相同

关键区别

  • != 比较的是是否相等。
  • is not 比较的是身份(内存地址)是否相同。

示例对比

x = [1, 2, 3]
y = [1, 2, 3]
z = x
print(x != y)  # False,因为 x 和 y 的值相等
print(x is not y)  # True,因为 x 和 y 是不同的对象
print(x != z)  # False,因为 x 和 z 的值相等
print(x is not z)  # False,因为 x 和 z 是同一个对象

总结

  • 使用 != 来比较两个对象的值是否不同。
  • 使用 is not 来比较两个对象是否是同一个对象(即它们是否指向相同的内存地址)。

CycleGAN

real_A:表示输入的真实的A图片
real_B:表示输入的真实的B图片
fake_B:表示真实图片A生成的假冒B风格图片
fake_A:表示真实图片B生成的假冒A风格图片
rec_A:表示fake_B再生成回A风格图片
rec_B:表示fake_A再生成回B风格图片
idt_B:表示真实图片A生成的A风格图片
idt_A:表示真实图片B生成的B风格图片

其中fake_B一般就是想要生成的图片

netG_A:生成器A,用于生成B风格的图片
netG_B:生成器B,用于生成A风格的图片
————————————————

C#

成员

类型的成员包括所有方法、字段、常量、属性和事件。 C# 没有全局变量或方法,这一点其他某些语言不同。 即使是编程的入口点(Main 方法),也必须在类或结构中声明(使用顶级语句时,隐式声明)。

下面列出了所有可以在类、结构或记录中声明的各种成员。

  • 字段Fields
  • 常量
  • 属性
  • 方法
  • 构造函数
  • 事件
  • 终结器
  • 索引器
  • 运算符
  • 嵌套类型

JIT (Just-In-Time – 实时编译) 和 AOT (Ahead-Of-Time – 预先编译)

C# 10 中可使用文件范围的命名空间声明。

namespace SampleNamespace;

what?

歃血为盟 [ shà xuè wéi méng ]

睚眦必报 [ yá zì bì bào ]

后顾之虞 hòu gù zhī yú

沆瀣一气 [ hàng xiè yī qì ]

已臻化境 yǐ zhēnhuàjìng

风光旖旎 fēng guāng yǐ nǐ