Python 裝飾器入門(下)

来源:https://www.cnblogs.com/flashBoxer/archive/2018/10/29/9873918.html
-Advertisement-
Play Games

繼續上次的進度:https://www.cnblogs.com/flashBoxer/p/9847521.html 正文: 裝飾類 在類中有兩種不通的方式使用裝飾器,第一個和我們之前做過的函數非常相似:在類的方法上應用。這也是當時引入裝飾器的原因之一一些常用的裝飾器已經內置到python中,像@cl ...


繼續上次的進度:https://www.cnblogs.com/flashBoxer/p/9847521.html

正文:

裝飾類

在類中有兩種不通的方式使用裝飾器,第一個和我們之前做過的函數非常相似:在類的方法上應用。這也是當時引入裝飾器的原因之一

一些常用的裝飾器已經內置到python中,像@classmethod @staticmethod @property。這三個裝飾器我們之前都介紹過,這段就不翻譯了(打字手酸,偷懶下)

下麵的Circle 類使用了@classmethod @staticmethod和@property三個裝飾器

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        """Get value of radius"""
        return self._radius

    @radius.setter
    def radius(self, value):
        """Set radius, raise error if negative"""
        if value >= 0:
            self._radius = value
        else:
            raise ValueError("Radius must be positive")

    @property
    def area(self):
        """Calculate area inside circle"""
        return self.pi() * self.radius**2

    def cylinder_volume(self, height):
        """Calculate volume of cylinder with circle as base"""
        return self.area * height

    @classmethod
    def unit_circle(cls):
        """Factory method creating a circle with radius 1"""
        return cls(1)

    @staticmethod
    def pi():
        """Value of π, could use math.pi instead though"""
        return 3.1415926535

在這個類中
    .cylinder_volume()是一個常規函數
    .radius是一個可變屬性:它可以被設置不同的值.然而通過定義setter方法,我們可以做一些判斷來確保它不會被設置成一個沒有意義的負數,.radius作為屬性訪問,不使用括弧
    .area 是一個不可變的屬性:沒有.setter()方法的屬性是無法更改的,即使它被定義為一個方法,它也被作為不需要括弧的屬性來使用。
    .unit_circle() 是一個類方法。它不被綁定到Circle的實例上.類方法通常用在工廠模式,用來創建類的特殊實例
    .pi() 是一個靜態方法.除了命名空間外它不依賴Circle類。靜態方法可以在實例或類上調用。

 

Circle類的使用例子:

>>> c = Circle(5)
>>> c.radius
5

>>> c.area
78.5398163375

>>> c.radius = 2
>>> c.area
12.566370614

>>> c.area = 100
AttributeError: can't set attribute

>>> c.cylinder_volume(height=4)
50.265482456

>>> c.radius = -1
ValueError: Radius must be positive

>>> c = Circle.unit_circle()
>>> c.radius
1

>>> c.pi()
3.1415926535

>>> Circle.pi()
3.1415926535

讓我們定義一個類,在這個類中,我們會用到前面的@debug和@timer裝飾器:

from decorators import debug, timer

class TimeWaster:
    @debug
    def __init__(self, max_num):
        self.max_num = max_num

    @timer
    def waste_time(self, num_times):
        for _ in range(num_times):
            sum([i**2 for i in range(self.max_num)])

看一下結果:

>>> tw = TimeWaster(1000)
Calling __init__(<time_waster.TimeWaster object at 0x7efccce03908>, 1000)
'__init__' returned None

>>> tw.waste_time(999)
Finished 'waste_time' in 0.3376 secs

另外一種方式是在整個類上使用裝飾器.這裡有個Python3.7中的dataclasses方法用例:

from dataclasses import dataclass

@dataclass
class PlayingCard:
    rank: str
    suit: str

語法的類似於函數裝飾器。在上面的例子中,也可以通過PlayingCard = dataclass(PlayingCard)來實現。

類裝飾器的一種簡單用法是作為元類方式的替代.在兩種情況下,你都在動態的改變一個類的定義

類的裝飾器和函數的裝飾器語法接近,不同的是裝飾器需要接收一個類而不是一個函數作為參數.事實上,上面的裝飾器都可以作用於類,但當你這麼用的時候,你可能得不到預期的結果。下麵將@timer裝飾器應用到一個類

from decorators import timer

@timer
class TimeWaster:
    def __init__(self, max_num):
        self.max_num = max_num

    def waste_time(self, num_times):
        for _ in range(num_times):
            sum([i**2 for i in range(self.max_num)])

@timer只是TimeWaster = timer(TimeWaster)的縮寫


在這裡@timer只能顯示類實例化需要的時間

>>> tw = TimeWaster(1000)
Finished 'TimeWaster' in 0.0000 secs

>>> tw.waste_time(999)
>>>

在後面會有一個正確的類裝飾器的示例@singleton。它保證一個類只有一個實例


嵌套的裝飾器


可以將多個裝飾器疊加到一個函數上

from decorators import debug, do_twice

@debug
@do_twice
def greet(name):
    print(f"Hello {name}")

運行的順序會按照疊加的順序, @debug 調用 @do_twice @do_twice 調用greet(),或者debug(do_twice(greet()))  

>>> greet("Eva")
Calling greet('Eva')
Hello Eva
Hello Eva
'greet' returned None        

更改@debug和@do_twice的順序:

from decorators import debug, do_twice

@do_twice
@debug
def greet(name):
    print(f"Hello {name}")    

在這種情況下,@do_twice也會被應用到@debug中: 

>>> greet("Eva")
Calling greet('Eva')
Hello Eva
'greet' returned None
Calling greet('Eva')
Hello Eva
'greet' returned None       

帶參數的裝飾器

 在需要傳參給你的裝飾器是這個例子會非常有用。例如,@do_twice可以擴展到@repeat(num_times)裝飾器.然後,可以將執行的被裝飾函數的次數作為參數給出。      
可以這麼做:

@repeat(num_times=4)
def greet(name):
    print(f"Hello {name}")      
        
>>> greet("World")
Hello World
Hello World
Hello World
Hello World        

       
考慮下如何實現這個功能

到目前為止,寫在@後面寫的名字引用一個可以被另外一個函數調用的函數對象,需要repeat(num_times=4)來返回一個函數對象,這個對象可以被作為裝飾器,幸運的是,我們已經知道如何返回函數!一般來說,需要以下內容:

def repeat(num_times):
    def decorator_repeat(func):
        ...  # Create and return a wrapper function
    return decorator_repeat

通常,裝飾器創建並返回一個內部包裝函數,所以完整地寫出這個例子會給你一個內部函數

def repeat(num_times):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper_repeat
    return decorator_repeat

例子看起來有點亂,但我們只是添加了一個def來接收參數,這個裝飾器語法我們之前處理過多次.讓我們從最裡面的函數開始:

def wrapper_repeat(*args, **kwargs):
    for _ in range(num_times):
        value = func(*args, **kwargs)
    return value

wrapper_repeat()函數接收任意參數,並放回被裝飾函數的值,func(). 這個包裝函數還包括了被裝飾函數num_times的迴圈 ,除了必須要使用外部參數num_times外,和之前看到的裝飾器函數沒有什麼不同,

再走一步,你就會發現裝飾器函數:

def decorator_repeat(func):
    @functools.wraps(func)
    def wrapper_repeat(*args, **kwargs):
        ...
    return wrapper_repeat

decorator_repeat()和我們之前寫的裝飾器函數非常像,除了他的名字不同,因為我們為最外層的函數保留了基礎名稱repeat(),這個是用戶要調用的函數。

最外層返回裝飾器函數的引用

def repeat(num_times):
    def decorator_repeat(func):
        ...
    return decorator_repeat

在repeat()中有一些細節:

        將decorator_repeat()作為一個內部函數意味著repeat()將引用一個函數對象-decotator_repeat.之前,我們用沒有括弧的repeat來引用函數對象.定義帶有參數的裝飾器,就需要添加括弧
        
        num_times參數看起來沒有在repeat()本身中使用,但是通過傳遞num_times,會創建一個閉包,來存儲num_times的值,直到wrapper_repeat()使用它為止。
    
一切就緒後,讓我們看看結果:

@repeat(num_times=4)
def greet(name):
    print(f"Hello {name}")

>>> greet("World")
Hello World
Hello World
Hello World
Hello World


這是我們想要的結果

Both Please, But Never Mind the Bread

稍微註意下.你可以把裝飾器同時定義為帶參數或者不帶參數.你可能不需要這樣,但更有靈活性也不錯

前面已經看到,當裝飾器需要參數的時候,需要有一個額外的外部函數,困難在於,代碼需要知道裝飾器是否被調用了,是否有參數

因為只有在沒有參數的情況下調用裝飾器時才會直接傳遞裝飾的函數,這個函數必須是可選參數.意味著裝飾器參數必須要友關鍵字指定,可以使用特殊的*,也就是說,下麵的參數都是關鍵字

def name(_func=None, *, kw1=val1, kw2=val2, ...):  # 1
    def decorator_name(func):
        ...  # Create and return a wrapper function.

    if _func is None:
        return decorator_name                      # 2
    else:
        return decorator_name(_func)               # 3

_func參數是一個標記,提示裝飾器被調用的時候是否有參數
    1.如果name調用的時候沒有傳參,被裝飾函數會被作為_func傳入.如果有參數傳入,_func會被置為None,一些關鍵字參數可能已不再是預設值, 參數列表中的*表示其餘參數不能作為位置參數調用。

    2.裝飾器可以傳參調用,返回一個裝飾器函數,它可以讀取和返回一個函數
    
    3.裝飾器不可以傳參調用,會只將裝飾器應用到函數上

改造下之前的@repeat裝飾器

def repeat(_func=None, *, num_times=2):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper_repeat

    if _func is None:
        return decorator_repeat
    else:
        return decorator_repeat(_func)

和之前的對比,唯一的變化是在末尾添加了_func參數和if-else。
這些例子表明,@repeat現在可以在有或沒有參數的情況下使用:

@repeat
def say_whee():
    print("Whee!")

@repeat(num_times=3)
def greet(name):
    print(f"Hello {name}")

預設情況num_times的值是2

>>> say_whee()
Whee!
Whee!

>>> greet("Penny")
Hello Penny
Hello Penny
Hello Penny

有狀態的裝飾器

有時候,可以跟蹤狀態的裝飾器也是很有用的.一個簡單的例子,我們會創建一個統計函數調用次數的裝飾器

註意:在教程的前面,我們討論了基於給定參數返回值的純函數.有狀態的裝飾器正好相反,返回值取決於當前狀態以及給定的參數。

在下一節中,您將看到如何使用類來保持狀態。但在簡單的情況下,也可以使用函數屬性:

import functools

def count_calls(func):
    @functools.wraps(func)
    def wrapper_count_calls(*args, **kwargs):
        wrapper_count_calls.num_calls += 1
        print(f"Call {wrapper_count_calls.num_calls} of {func.__name__!r}")
        return func(*args, **kwargs)
    wrapper_count_calls.num_calls = 0
    return wrapper_count_calls

@count_calls
def say_whee():
    print("Whee!")

狀態——函數的調用次數——存儲在包裹函數(wrapper_count_calls)的函數屬性.num_calls中。下麵是使用它的效果:

>>> say_whee()
Call 1 of 'say_whee'
Whee!

>>> say_whee()
Call 2 of 'say_whee'
Whee!

>>> say_whee.num_calls
2

類裝飾器

典型的維護狀態的方式是使用類。在本節中,將看到如何重寫@count_calls的例子來實現類裝飾器

回想一下,裝飾器語法@my_decorator只是func = my_decorator(func)一種方便快捷的用法.因此,如果my_decorator是一個類,需要在它的.__init__方法中接收func作為一個參數.而且,這個類需要是可以被調用的,這樣它就可以替代裝飾器函數了

如果需要一個類可以被調用,要實現.__call__方法(看示例:https://www.cnblogs.com/flashBoxer/tag/python/)

class Counter:
    def __init__(self, start=0):
        self.count = start

    def __call__(self):
        self.count += 1
        print(f"Current count is {self.count}")

.__call__方法每次運行都會嘗試調用一個類的實例:

>>> counter = Counter()
>>> counter()
Current count is 1

>>> counter()
Current count is 2

>>> counter.count
2

因此,實現類裝飾器需要實現.__init__和.__call__

import functools

class CountCalls:
    def __init__(self, func):
        functools.update_wrapper(self, func)
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"Call {self.num_calls} of {self.func.__name__!r}")
        return self.func(*args, **kwargs)

@CountCalls
def say_whee():
    print("Whee!")

.__init__方法必須可以存儲一個函數的引用和能夠做一些必要的初始化. 調用.__call__方法來替代裝飾器函數.它做的和我們之前的 wrapper()函數基本一樣,註意,這裡使用functools.update_wrapper()函數,而不是@functools.wraps

這個@CountCalls裝飾器的工作原理與前一節相同:

>>> say_whee()
Call 1 of 'say_whee'
Whee!

>>> say_whee()
Call 2 of 'say_whee'
Whee!

>>> say_whee.num_calls
2

 

更多現實中的例子


我們已經學到了很多(看了下翻譯的行數量,已經1K+了,確實很多),已經學會如何創建各種各樣的裝飾師,把我們的新知識應用到創建更多的示例中,這些示例在現實中可能非常有用。


代碼降速,重新訪問


我們之前實現的@slow_down一直是保持sleep 1秒.現在你知道瞭如何給裝飾器添加參數,因此,讓我們來重寫@slow_down,使用一個可選的rate參數來控制它的sleep時間:

import functools
import time

def slow_down(_func=None, *, rate=1):
    """Sleep given amount of seconds before calling the function"""
    def decorator_slow_down(func):
        @functools.wraps(func)
        def wrapper_slow_down(*args, **kwargs):
            time.sleep(rate)
            return func(*args, **kwargs)
        return wrapper_slow_down

    if _func is None:
        return decorator_slow_down
    else:
        return decorator_slow_down(_func)

我們使用  Both Please, But Never Mind the Bread  這裡的樣例來讓@slow_down有參數和沒有參數時都可調用,countdown()函數現在在每次計數之間休眠2秒:

@slow_down(rate=2)
def countdown(from_number):
    if from_number < 1:
        print("Liftoff!")
    else:
        print(from_number)
        countdown(from_number - 1

和前面一樣,你最好自己寫寫,跑下看看結果

>>> countdown(3)
3
2
1
Liftoff!

創建單例模式

單例模式是一個只有一個實例的類.在Python經常使用的單例對象包括None,True和False.可以使用is來比較,像我們之前在Both Please的章節中:

if _func is None:
    return decorator_name
else:
    return decorator_name(_func)

is只對完全相同實例的對象返回True。下麵的@singleton裝飾器將類的第一個實例存儲為屬性,從而將類轉換為單例對象。之後創建實例只是返回已經存儲的實例:

import functools

def singleton(cls):
    """Make a class a Singleton class (only one instance)"""
    @functools.wraps(cls)
    def wrapper_singleton(*args, **kwargs):
        if not wrapper_singleton.instance:
            wrapper_singleton.instance = cls(*args, **kwargs)
        return wrapper_singleton.instance
    wrapper_singleton.instance = None
    return wrapper_singleton

@singleton
class TheOne:
    pass

這個類裝飾器和我們的函數裝飾器基本一樣.唯一不同的地方在於使用cls代替了fun來表示這是一個類裝飾器

看下運行結果:

>>> first_one = TheOne()
>>> another_one = TheOne()

>>> id(first_one)
140094218762280

>>> id(another_one)
140094218762280

>>> first_one is another_one
True


很明顯,first_one確實與另一個實例完全相同。

緩存返回值

裝飾器可以提供很方便的緩存和記憶機制.作為一個例子,我們來看看斐波那契數列的遞歸定義:

from decorators import count_calls

@count_calls
def fibonacci(num):
    if num < 2:
        return num
    return fibonacci(num - 1) + fibonacci(num - 2)

實現很簡單,性能很糟糕

>>> fibonacci(10)
<Lots of output from count_calls>
55

>>> fibonacci.num_calls
177

為了計算第10個斐波那契數,你實際上只需要計算前面的斐波那契數,但是這個實現需要177次計算。更糟糕的是:斐波納契數列(20)需要21891次計算,第30次需要270萬次計算。這是因為代碼一直在重新計算已知的斐波那契數。

通常的解決方案是使用for迴圈和查找表來實現斐波那契數。但是,簡單的計算緩存也可以做到這一點:

import functools
from decorators import count_calls

def cache(func):
    """Keep a cache of previous function calls"""
    @functools.wraps(func)
    def wrapper_cache(*args, **kwargs):
        cache_key = args + tuple(kwargs.items())
        if cache_key not in wrapper_cache.cache:
            wrapper_cache.cache[cache_key] = func(*args, **kwargs)
        return wrapper_cache.cache[cache_key]
    wrapper_cache.cache = dict()
    return wrapper_cache

@cache
@count_calls
def fibonacci(num):
    if num < 2:
        return num
    return fibonacci(num - 1) + fibonacci(num - 2)

緩存作為查找表工作,所以現在fibonacci()只執行一次計算:

>>> fibonacci(10)
Call 1 of 'fibonacci'
...
Call 11 of 'fibonacci'
55

>>> fibonacci(8)
21

註意,在對fibonacci(8)的最後調用中,沒有進行新的計算,因為fibonacci(10)已經計算了第8個fibonacci數。
在標準庫中,提供了@functools.lru_cache。

這個裝飾器比上面的例子要具備更多特性.我們應該使用@functools.lru_cache來代替我們自己寫的緩存裝飾器

import functools

@functools.lru_cache(maxsize=4)
def fibonacci(num):
    print(f"Calculating fibonacci({num})")
    if num < 2:
        return num
    return fibonacci(num - 1) + fibonacci(num - 2)

maxsize參數指定緩存了多少次調用。預設值是128,但是可以指定maxsize=None來緩存所有函數調用。但是,請註意,如果正在緩存許多很大的對象,這可能會導致記憶體問題。

可以使用.cache_info()方法查看緩存的執行情況,併在需要時進行調優。在我們的示例中,我們設定一個小maxsize來查看從緩存中刪除元素的效果:

>>> fibonacci(10)
Calculating fibonacci(10)
Calculating fibonacci(9)
Calculating fibonacci(8)
Calculating fibonacci(7)
Calculating fibonacci(6)
Calculating fibonacci(5)
Calculating fibonacci(4)
Calculating fibonacci(3)
Calculating fibonacci(2)
Calculating fibonacci(1)
Calculating fibonacci(0)
55

>>> fibonacci(8)
21

>>> fibonacci(5)
Calculating fibonacci(5)
Calculating fibonacci(4)
Calculating fibonacci(3)
Calculating fibonacci(2)
Calculating fibonacci(1)
Calculating fibonacci(0)
5

>>> fibonacci(8)
Calculating fibonacci(8)
Calculating fibonacci(7)
Calculating fibonacci(6)
21

>>> fibonacci(5)
5

>>> fibonacci.cache_info()
CacheInfo(hits=17, misses=20, maxsize=4, currsize=4)

添加單元信息

下麵的示例與前面的Registering Plugins示例有點類似,因為它不會真正改變被裝飾函數的行為。相反,它只是將unit添加為函數屬性:

def set_unit(unit):
    """Register a unit on a function"""
    def decorator_set_unit(func):
        func.unit = unit
        return func
    return decorator_set_unit

下麵的示例根據圓柱體的半徑和高度(以釐米為單位)來計算體積:

import math

@set_unit("cm^3")
def volume(radius, height):
    return math.pi * radius**2 * height   
    
這個.unit函數屬性是可以訪問的:    
>>> volume(3, 5)
141.3716694115407

>>> volume.unit
'cm^3' 

註意,可以使用函數註釋實現類似的功能: 

import math

def volume(radius, height) -> "cm^3":
    return math.pi * radius**2 * height   

但是,由於註釋用於類型提示,因此很難將註釋和靜態類型檢查相結合。   
    
在連接到一個能夠在單位間轉換的庫,單位可以變得更加強大和有趣.pip install pint,  您可以將體積轉換為立方英寸或加侖: 

>>> import pint
>>> ureg = pint.UnitRegistry()
>>> vol = volume(3, 5) * ureg(volume.unit)

>>> vol
<Quantity(141.3716694115407, 'centimeter ** 3')>

>>> vol.to("cubic inches")
<Quantity(8.627028576414954, 'inch ** 3')>

>>> vol.to("gallons").m  # Magnitude
0.0373464440537444  


你還可以修改裝飾器來直接返回一個Pint數量.數量是通過與單位相乘得到的,在pint中,units必須只能在UnitRegistry中查詢.這裡註冊用來存儲函數屬性來避免命名空間混亂

def use_unit(unit):
    """Have a function return a Quantity with given unit"""
    use_unit.ureg = pint.UnitRegistry()
    def decorator_use_unit(func):
        @functools.wraps(func)
        def wrapper_use_unit(*args, **kwargs):
            value = func(*args, **kwargs)
            return value * use_unit.ureg(unit)
        return wrapper_use_unit
    return decorator_use_unit

@use_unit("meters per second")
def average_speed(distance, duration):
    return distance / duration


使用@use_unit裝飾器,轉換單位實際上是很容易

>>> bolt = average_speed(100, 9.58)
>>> bolt
<Quantity(10.438413361169102, 'meter / second')>

>>> bolt.to("km per hour")
<Quantity(37.578288100208766, 'kilometer / hour')>

>>> bolt.to("mph").m  # Magnitude
23.350065679064745

驗證JSON

讓我們看最後一個用例。快速看下Flask路由的管理程式:

@app.route("/grade", methods=["POST"])
def update_grade():
    json_data = request.get_json()
    if "student_id" not in json_data:
        abort(400)
    # Update database
    return "success!"

這裡我們確保key student_id是請求的一部分.雖然驗證有效,但它實際上並不屬於函數本身.另外,可能還有其他使用相同驗證的路由。因此,讓我們Don't repeat yourself,來使用裝飾器抽象出任何不必要的邏輯,下麵的@validate_json裝飾器會完成這個工作:

from flask import Flask, request, abort
import functools
app = Flask(__name__)

def validate_json(*expected_args):                  # 1
    def decorator_validate_json(func):
        @functools.wraps(func)
        def wrapper_validate_json(*args, **kwargs):
            json_object = request.get_json()
            for expected_arg in expected_args:      # 2
                if expected_arg not in json_object:
                    abort(400)
            return func(*args, **kwargs)
        return wrapper_validate_json
    return decorator_validate_json

在上面的代碼中,裝飾器採用了一個可變長度列表作為參數,這樣我們就可以傳遞儘可能多的字元串參數,每個參數都代表一個用於驗證JSON數據的鍵:

    1.json的keys列表作為參數傳遞給裝飾器
    2.包裹函數驗證JSON數據中出現的每個預期鍵

然後,路由管理程式可以關註其真正的業務級別——因為它可以安全地假設JSON數據是有效的:

@app.route("/grade", methods=["POST"])
@validate_json("student_id")
def update_grade():
    json_data = request.get_json()
    # Update database.
    return "success!"

 

 

結束語:翻譯就到這裡吧,這篇文章的作者對裝飾器的理解很是深入,文章很長,翻起來確實花了不少時間。文中如果有翻譯不穩妥的地方,請留言給我。最後老鐵們如果覺得對理解python的裝飾器有幫助,右下角點個贊吧,結尾附上原文地址:https://realpython.com/primer-on-python-decorators/

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 題意 "題目鏈接" Sol 考場上做完前四題的時候大概還剩半個小時吧,那時候已經困的不行了。 看了看E發現好像很可做?? 又仔細看了幾眼發現這不是sb題麽。。。 先考慮兩個人,假設貢獻分別為$(x, y) (a, b)$ 有兩種組合方式,一種是$x + b$,另一種是$y + a$ 若$x + b ...
  • 1. 有如下變數(tu 是個元祖),請實現要求的功能 tu = {"alex",[11,22,{"k1":'v1',"k2":["age","name"],"k3":(11,22,33)},44]} 2. 字典 dic,dic={'k1':"v1",'k2':"v2",'k3':[11,22,33] ...
  • JDBC 筆記 作者:晨鐘暮鼓c個人微信公眾號:程式猿的月光寶盒 Day1 JDBC概述+JDBC完成CRUD+DAO設計 1.JDBC概述 1.1 什麼是持久化( persistence ): 持久化(persistence):把數據保存到可掉電式存儲設備中以供之後使用。 ​ 保存數據: ​ 記憶體 ...
  • PRC原理 RPC 遠程過程調用(Remote Procedure Call) 一般用來實現部署在不同機器上的系統之間的方法調用,使得程式能夠像訪問本地系統資源一樣,通過網路傳輸去訪問遠程系統資源,RPC框架實現的原理都是類似的,如下圖: Client Code:客戶端調用方代碼實現,負責發起RPC ...
  • 字元串的創建 字元串創建符號 ' ' " " ''' ''' """ """ 轉義符\ >>> string_long = """This is another long string ... value that will span multiple ... lines in the output ...
  • 一、對Redis持久化的探討與理解 目前Redis持久化的方式有兩種: RDB 和 AOF 首先,我們應該明確持久化的數據有什麼用,答案是用於重啟後的數據恢復。 Redis是一個記憶體資料庫,無論是RDB還是AOF,都只是其保證數據恢復的措施。 所以Redis在利用RDB和AOF進行恢復的時候,都會讀 ...
  • Python爬蟲目前是基於requests包,下麵是該包的文檔,查一些資料還是比較方便。 http://docs.python-requests.org/en/master/ 爬取某旅游網站的產品評論,通過分析,獲取json文件需要POST指令。簡單來說: GET是將需要發送的信息直接添加在網址後面 ...
  • 1、基礎介紹 常用功能 1、HTTP服務 動靜分離、WEB緩存、虛擬主機設置、URL Rewrite 2、負載均衡 3、反向代理 4、正向代理 5、郵件伺服器 優點 高擴展、高可用、支持高併發、低資源消耗、可平滑升級重啟(熱部署) 2、安裝部署 1、下載 nginx-1.15.3.tar.gz 2、 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...