翻译:PropertyWrapper swift 5 aop特性

翻译:PropertyWrapper swift 5 aop特性

说明

swift 5 提出PropertyWrapper,类似于AOP拦截器的思想,在属性的更改之前,做个拦截处理,以下为。

PropertyWrapper

属性包装器在管理属性存储方式的代码与定义属性的代码之间增加了一层隔离。例如,如果您具有提供线程安全检查或将其基础数据存储在数据库中的属性,则必须在每个属性上编写该代码。使用属性包装器时,定义包装器时,只需编写一次管理代码,然后通过将其应用于多个属性来重用该管理代码。

要定义属性包装器,您需要创建定义属性的结构,枚举或类wrappedValue。在下面的代码中,该TwelveOrLess结构确保包装的值始终包含小于或等于12的数字。如果您要求存储更大的数字,则改为存储12。

@propertyWrapper
struct TwelveOrLess {
    private var number: Int
    init() { self.number = 0 }
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}

设置器确保新值小于12,并且getter返回存储的值。

笔记

上例中的声明number将变量标记为private,以确保number仅在的实现中使用TwelveOrLess。在其他地方编写的代码使用的getter和setter访问值wrappedValue,并且不能number直接使用。有关的信息private,请参阅访问控制。

通过在属性之前写包装器的名称作为属性,将包装器应用于属性。这是一个存储一个小矩形的结构,它使用由TwelveOrLess属性包装器实现的“ small”相同(相当随意)的定义:

struct SmallRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}

var rectangle = SmallRectangle()
print(rectangle.height)
// Prints "0"

rectangle.height = 10
print(rectangle.height)
// Prints "10"

rectangle.height = 24
print(rectangle.height)
// Prints "12"

在height和width来自定义性能得到它们的初始值TwelveOrLess,它设置TwelveOrLess.number为零。rectangle.height因为数字很小,所以将数字10存储到成功。尝试存储24实际上存储的是12的值,因为对于属性设置程序的规则而言24太大了。

将包装器应用于属性时,编译器将合成为包装器提供存储的代码和提供通过包装器访问属性的代码。(属性包装器负责存储包装后的值,因此没有为此合成的代码。)您可以编写使用属性包装器的行为的代码,而无需利用特殊的属性语法。例如,这是SmallRectangle先前代码清单的的一个版本,该版本将其属性TwelveOrLess显式地包装在结构中,而不是@TwelveOrLess作为属性来编写:

struct SmallRectangle {
    private var _height = TwelveOrLess()
    private var _width = TwelveOrLess()
    var height: Int {
        get { return _height.wrappedValue }
        set { _height.wrappedValue = newValue }
    }
    var width: Int {
        get { return _width.wrappedValue }
        set { _width.wrappedValue = newValue }
    }
}

在_height和_width属性存储属性包装的一个实例,TwelveOrLess。获取height和width包装对wrappedValue属性的访问权的setter和setter 。

设置包装属性的初始值
上面示例中的代码通过number在的定义中提供初始值来设置wrapd属性的初始值TwelveOrLess。使用此属性包装器的代码不能为被包装的属性指定其他初始值,TwelveOrLess例如,SmallRectangle不能给出height或width初始值的定义。为了支持设置初始值或其他自定义,属性包装器需要添加一个初始化程序。下面是一个扩大版TwelveOrLess叫SmallNumber那一套包裹和最大值定义初始化:

@propertyWrapper
struct SmallNumber {
    private var maximum: Int
    private var number: Int

    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, maximum) }
    }

    init() {
        maximum = 12
        number = 0
    }
    init(wrappedValue: Int) {
        maximum = 12
        number = min(wrappedValue, maximum)
    }
    init(wrappedValue: Int, maximum: Int) {
        self.maximum = maximum
        number = min(wrappedValue, maximum)
    }
}

的定义SmallNumber包括三个初始化器init(),init(wrappedValue:)和init(wrappedValue:maximum:),下面的示例使用这些初始化器来设置包装后的值和最大值。有关初始化和初始化程序语法的信息,请参见Initialization。

当您将包装器应用于属性并且未指定初始值时,Swift使用init()初始化程序来设置包装器。例如:

struct ZeroRectangle {
    @SmallNumber var height: Int
    @SmallNumber var width: Int
}

var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)
// Prints "0 0"

的情况下,SmallNumber该包裹height并width通过调用创建SmallNumber()。该初始化中的代码设置初始包裹值和初始最大值,使用的零和12的默认值的属性包装仍然提供所有的初始值的,如所使用的前面的例子TwelveOrLess在SmallRectangle。与该示例不同,SmallNumber它还支持编写那些初始值作为声明属性的一部分。

当您为属性指定初始值时,Swift使用init(wrappedValue:)初始化程序来设置包装器。例如:

struct UnitRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber var width: Int = 1
}

var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)
// Prints "1 1"

当您使用包装器写属性时,该属性将转换为对初始化器的调用。的情况下,该包裹并通过调用创建。初始化程序使用此处指定的包装值,并且使用默认最大值12。= 1init(wrappedValue:)SmallNumberheightwidthSmallNumber(wrappedValue: 1)

当您在自定义属性后的括号中写入参数时,Swift将使用接受这些参数的初始化程序来设置包装器。例如,如果您提供一个初始值和一个最大值,Swift将使用init(wrappedValue:maximum:)初始化程序:

struct NarrowRectangle {
    @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
    @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}

var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)
// Prints "2 3"

narrowRectangle.height = 100
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
// Prints "5 4"

的实例SmallNumber,它包装height是通过调用创建,以及包装的实例是通过调用创建。SmallNumber(wrappedValue: 2, maximum: 5)widthSmallNumber(wrappedValue: 3, maximum: 4)

通过包含属性包装器的参数,可以在包装器中设置初始状态,或者在创建包装器时将其他选项传递给包装器。此语法是使用属性包装器的最通用方法。您可以为属性提供所需的任何参数,然后将它们传递给初始化程序。

当包含属性包装器参数时,还可以使用赋值指定初始值。Swift将分配视为一个wrappedValue参数,并使用接受您所包含的参数的初始化程序。例如:

struct MixedRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber(maximum: 9) var width: Int = 2
}

var mixedRectangle = MixedRectangle()
print(mixedRectangle.height)
// Prints "1"

mixedRectangle.height = 20
print(mixedRectangle.height)
// Prints "12"

SmallNumber包装的实例height是通过调用来创建的,该实例使用默认的最大值12。包装的实例是通过调用来创建的。SmallNumber(wrappedValue: 1)widthSmallNumber(wrappedValue: 2, maximum: 9)

从属性包装器投影值
除了包装的值之外,属性包装器还可以通过定义投影值来公开其他功能,例如,管理对数据库的访问的属性包装器可以flushDatabaseConnection()在其投影值上公开方法。预计值的名称与包装值相同,但以美元符号( ) 开 头 。 因 为 您 的 代 码 无 法 定 义 以 )开头。因为您的代码无法定义以 )开头。因为您的代码无法定义以投影值开头的属性,所以不会干扰您定义的属性。

在SmallNumber上面的示例中,如果尝试将属性设置为太大的数字,则属性包装器将在存储数字之前对其进行调整。下面的代码projectedValue在SmallNumber结构中添加了一个属性,以在存储该新值之前跟踪该属性包装器是否调整了该属性的新值。

@propertyWrapper
struct SmallNumber {
    private var number: Int
    var projectedValue: Bool
    init() {
        self.number = 0
        self.projectedValue = false
    }
    var wrappedValue: Int {
        get { return number }
        set {
            if newValue > 12 {
                number = 12
                projectedValue = true
            } else {
                number = newValue
                projectedValue = false
            }
        }
    }
}
struct SomeStructure {
    @SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()

someStructure.someNumber = 4
print(someStructure.$someNumber)
// Prints "false"

someStructure.someNumber = 55
print(someStructure.$someNumber)
// Prints "true"

写入someStructure. s o m e N u m b e r 访 问 包 装 器 的 预 计 值 。 存 储 样 四 少 数 后 , 的 值 s o m e S t r u c t u r e . someNumber访问包装器的预计值。存储样四少数后,的值someStructure. someNumber访问包装器的预计值。存储样四少数后,的值someStructure.someNumber是false。但是,预计值是true在尝试存储太大的数字(如55 )之后得出的。

属性包装器可以返回任何类型的值作为其投影值。在此示例中,属性包装器仅公开一条信息(无论数字是否已调整),因此它公开该布尔值作为其投影值。需要公开更多信息的包装器可以返回其他某种数据类型的实例,也可以返回self以公开其包装器的实例作为其投影值。

当您从属于类型一部分的代码中访问投影值时(例如,属性获取器或实例方法),可以self.像访问其他属性一样在属性名之前省略。在以下示例中的代码是指围绕包装件的投影值height与width作为 h e i g h t 和 height和 height和width:

enum Size {
    case small, large
}

struct SizedRectangle {
    @SmallNumber var height: Int
    @SmallNumber var width: Int

    mutating func resize(to size: Size) -> Bool {
        switch size {
        case .small:
            height = 10
            width = 20
        case .large:
            height = 100
            width = 100
        }
        return $height || $width
    }
}

因为属性包装器语法只是具有getter和setter的属性的语法糖,所以访问height和width行为与访问任何其他属性相同。例如,resize(to:)访问中的代码height及其width使用的属性包装器。如果调用,开关盒将矩形的高度和宽度设置为100。包装器将防止这些属性的值大于12,并将其投影值设置为,以记录其调整其值的事实。最后,return语句检查并确定属性包装器是否已调整或。resize(to: .large).largetrueresize(to:) h e i g h t height heightwidthheightwidth

Read more

Python竟然这样处理文件?

Python竟然这样处理文件?

二进制文件的读取和写入 二进制文件的处理流程和文本文件流程一致。首先还是要创建文件对象,不过,我们需要指定二进制模式,从而创建出二进制文件对象。例如: f = open(r"d:\a.txt" 'wb') #可写的、重写模式的二进制文件对象``f = open(r"d:\a.txt", 'ab') #可写的、追加模式的二进制文件对象``f = open(r"d:\a.txt", 'rb') #可读的二进制文件对象 创建好二进制文件对象后,仍然可以使用 write() 、 read() 实现文件的读写操作。

By Ne0inhk
Python爬虫库urllib使用详解

Python爬虫库urllib使用详解

一、Python urllib库 Python urllib 库用于操作网页 URL,并对网页的内容进行抓取处理。 Python3 的 urllib。 urllib 包 包含以下几个模块: * urllib.request - 打开和读取 URL。 * urllib.error - 包含 urllib.request 抛出的异常。 * urllib.parse - 解析 URL。 * urllib.robotparser - 解析 robots.txt 文件。 需要用的就是每个模块的内置方法和函数。大概方法如下图: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RMqEk2hg-1692930788712)(https://mmbiz.qpic.cn/mmbiz_png/

By Ne0inhk
程序员看剧,如果看到编程场景,会暂停看代码吗?

程序员看剧,如果看到编程场景,会暂停看代码吗?

有网友在知乎上问: 今天在看剧的时候,突然有个疑问,因为现在的很多电视剧经常会有敲代码的页面出现,就想问下各位程序员大佬们,会在这个片段暂停看代码么?然后判断是真的还是假的之类的?代码对不对之类的? 会的,以前看到类似场景时,我是会暂停下来研究一下。 一起来看看有相同“爱好”的程序员同行遇到的那些影视剧的代码场景。 网友 Narsil: 《战狼1》中龙小云的入侵病毒的源代码。一共包括 5 段功能代码。 * 第一段是输出斐波那契数列的前 20 项。 * 第二段是找 100 到 200 之间的所有质数。 * 第三段是找 100 到 999 之间的所有水仙花数。 * 第四段是通过键盘输入一个整数并找出其所有因数。 * 第五段是根据键盘输入的分数评出 ABC 等级。 通过以上几段残缺不全的代码,成功入侵红军指挥系统,并导致对方系统瘫痪。 网友 厦小米 编剧视角的程序员是用 word 写代码的。放弃吧,写不写得出来,公司都要完了。 网友

By Ne0inhk
Python应该怎么学,如何系统地自学Python?

Python应该怎么学,如何系统地自学Python?

这是一份kaggle上的银行的数据集,研究该数据集可以预测客户是否认购定期存款y。这里包含20个特征。 1. 分析框架 2. 数据读取,数据清洗 # 导入相关包 import numpy as np import pandas as pd # 读取数据 data = pd.read_csv('./1bank-additional-full.csv') # 查看表的行列数 data.shape 输出: 这里只有nr.employed这列有丢失数据,查看下: data['nr.employed'].value_counts() 这里只有5191.0这个值,没有其他的,且只有7763条数据,这里直接将这列当做异常值,直接将这列直接删除了。 # data.drop('nr.

By Ne0inhk