类和对象

类和对象

Python被称为面向对象语言,所以面向对象技术支持非常完整,例如,类、接口、继承、封装、多态等。

8.1 对象的魔法
对象可以看作数据以及可以操作这些数据的一系列方法的集合。为了区分全局函数,将这些写在类中的函数称为方法。想要访问这些类中的函数,必须要对类实例化,实例化后的产物被称为对象。实例化后,调用方法时需要先指定对象名称,然后才可调用这些方法。

面向对象的三大特征:

继承:当前类从其他类获得资源(数据和方法),以便更好的重用代码,并且可以描述类与类之间的关系。
封装:对外部世界隐藏对象的工作细节。
多态:只有在程序运行时才确定具体的类,从而导致该引用调用的具体方法随之改变,让程序可以选择多个运行状态,这就是多态。
python中没有接口的概念,想要使用接口,就直接使用对象好了,而且假定要调用的对象成员都存在。( 当一个方法在很多类中有不同的体现的时候,这个时候就可以将这个方法抽象出来做成一个接口,即一组规范 )。

8.2 类
8.2.1 创建自己的类
创建一个类,以及利用这个类创建两个对象,并调用其中的方法。

# 创建一个Person类
class Person:
# 定义setName方法
def setName(self, name):
self.name = name
# 定义getName方法
def getName(self):
return self.name
# 定义greet方法
def greet(self):
print(“Hello, I’m {name}.”.format(name = self.name))

# ————-对类实例化——————-
# 对类实例化,实例化后的产物就是对象,即引用类时指定了一个变量,并分配了一块内存
person1 = Person()
person2 = Person()

# ————-给类中的变量赋值————–
# 调用类对象中的setName方法
person1.setName(“Bill Gates”)
# 调用类对象中的name属性
person2.name = “Bill Clinton”

# ————-调用类中的方法—————-
# 两种等价调用方式
print(person1.getName())
person1.greet()
# 两种等价调用方式
print(person2.name)
Person.greet(person2)

知识点:

python类使用class关键字定义,类名直接跟在class关键字的后面。
类名也是一个代码块,所以类名后面要跟着一个冒号(:).。
类中的方法其实就是函数,定义方式完全一样。
每一个方法的*个参数都是self,其实这是必须的。任何方法必须至少指定一个self参数,如果方法包含多个参数,*个参数将作为self参数使用。在调用方法时,这个参数值不需要自己传递。系统会将方法所属的对象传入这个参数。在方法内部可以利用这个参数调用对象本身的资源,如属性、方法等。
通过self参数添加的name变量是person类的属性,可以在外部访问。上例子中设置了person2对象的name属性值,与调用person.setName方法的效果完全相同。
调用对象的方法有两种方式。一种是直接通过对象变量调用方法,另一种是通过调用方法,并且将相应的对象传入方法的第1个参数。例如 Person.greet(person2)的方式调用了person2对象中的greet方法。
8.2.2 方法和私有化
python 类默认情况下,所有的方法都可以被外部访问。在python类中并没有提供private或类似的关键字将方法私有化,但可以迂回解决。方法私有化:只有类的内部方法才能访问私有化的方法,通过正常的方法是无法访问对象的私有化方法的,使用反射技术的除外。

在python类的方法名前面加双下划线(__)可以让该方法在外部不访问。

class person:
# 在外部可以访问
def method1(self):
print(“method1”)

# 在外部不可访问

def __method2(self):
print(“method2”)

def method3(self):
print(“method3”)
# 内部正常引用
self.__method2()

p = person()
p.method1()
p.method3()
p._person__method2() #外部正常引用
p.__method2()

p.__method2() 抛出异常,找不到相关类名

Traceback (most recent call last):
File “C:/Users/UFO/PycharmProjects/fullstack1/test.py”, line 14, in <module>
p.__method2()
AttributeError: ‘person’ object has no attribute ‘__method2’

原理分析:其实 __method2() 方法也不是*对不可访问的。python编译器在编译源代码时,并没有将 __method2() 方法私有化,而是一旦遇到方法名以双下划线开头的方法,就会将方法名改成 _ClassName__methodName 的形式。其中,ClassName表示该方法所在的类名,methodName表示方法名。

class MyClass:
def getName(self):
return self.name
def setName(self, name):
self.name = name
# 直接调用私有方法
self.__outName()

def __outName(self):
print(“Name = {}”.format(self.name))

myClass = MyClass()
# inspect模块中的getmembers方法可以输出myClass类中的所有成员方法,并输出方法名
import inspect
methods = inspect.getmembers(myClass, predicate=inspect.ismethod)
print(methods)

for method in methods:
print(method[0])

print(“————“)
myClass.setName(“Bill”)
print(myClass.getName())
myClass._MyClass__outName()
print(myClass.__outName())

8.2.3 类代码块
class语句与for、while语句一样,都是代码块,这就意味着,定义类其实就是执行代码块。

class person:
print(“person”)

# 输出结果:person

class MyClass:
print(“MyClass”)
count = 0
def counter(self):
self.count += 1

my = MyClass()
my.counter()
print(my.count)
my.counter()
print(my.count)
my.count = “abc”
print(my.count)
my.name = “Hello”
print(my.name)

执行结果

MyClass
1
2
abc
Hello

8.2.4 类的继承
所谓类的继承,就是指一个类(子类)从另外一个类(父类)中获得了所有的成员。父类的成员可以在子类中使用,就像子类本身的成员一样。(私有方法的调用另外考虑,取决于调用方法的名称方式)

class ParentClass:
name = 30
def method1(self):
print(“method1”)

# 类继承的定义方式
class ChildClass(ParentClass):
def method2(self):
print(“method2”)
print(self.name)

# 通过子类调用父类的方法
child = ChildClass()
child.method1()
child.method2()

8.2.5 检测继承关系
在很多场景中,需要知道一个类A是否是从另外一个类B继承的,这种效验主要是为了调用B类中的成员(方法和属性)。关键是要判断B是否为A的父类。

如果要判断类与类之间的关系可以使用 issubclass函数,该函数接收两个参数,第1个参数是子类,第2个参数是父类。如果第1个参数指定的类与第2个参数指定的类确实是继承关系,那么该函数返回True,否则返回False。

如果想要获得已知类的父类(可能有多个父类),可以直接使用 bases ,这是类的一个特殊属性。

也可以使用 isinstance 函数检测一个对象是否是某一个类的实例。isinstance函数有两个参数,第1个参数是要检测的对象,第2个参数是一个类。如果第1个参数指定的对象是第2个参数指定的类的实例,那么该函数返回True,否则返回False。

# 父类(爷爷)
class MyParentClass:
def method(self):
return 50
# 子类(爸爸)
class ParentClass(MyParentClass):
def method1(self):
print(“method1”)
class MyClass:
def method(self):
return 40
# 子类(儿子)
class ChildClass(ParentClass):
def method2(self):
print(“method2”)

# 判断继承关系,可以判断包括多层的继承关系
print(issubclass(ChildClass, ParentClass))
print(issubclass(ChildClass, MyClass))
print(issubclass(ChildClass, MyParentClass))

# 仅仅得到上一层的父类名称,不会输出多层父类名称
print(ChildClass.__bases__)
print(ParentClass.__bases__)

# 判断是否是类的实例,可以判断多层继承的类的实例关系
child = ChildClass()
print(isinstance(child, ChildClass))
print(isinstance(child, ParentClass))
print(isinstance(child, MyParentClass))
print(isinstance(child, MyClass))

8.2.6 多继承
python支持多继承。想要为某个类指定多个父类,需要在类名称后边的圆括号中设置。多个父类名之间用逗号(,)分隔。

如果多个父类中有相同的成员,例如,在两个或两个以上父类中有同名的方法,那么会按照父类书写的顺序继承。也就是说,写在前面的父类会覆盖写在后面的父类同名的方法。在python类中,不会根据方法参数个数和数据类型进行重载。

class Calculator:
def calculate(self,expression):
self.value = eval(expression)
def printResult(self):
print(“result:{}”.format(self.value))

class MyPrint:
def printResult(self):
print(“计算结果:{}”.format(self.value))
def aa(self,a):
return 30

# Calculator 在 MyPrint 前面,所以Calculator中的printResult方法会生效
class NewCalculator(Calculator, MyPrint):
# 如果类中没有代码,需要添加 pass
pass

# MyPrint 在 Calculator 前面,所以MyPrint中的printResult方法会生效
class NewCalculator1(MyPrint,Calculator):
# 如果类中没有代码,需要添加 pass
pass

# 多继承,并调用父类方法,优先的同名方法
calc = NewCalculator()
calc.calculate(“1 + 3 * 5”)
calc.printResult()
# 仅仅输出上层继承的多个类的名称,不是输出多层继承的父类名
print(NewCalculator.__bases__)

# 多继承,并调用父类方法,优先的同名方法
calc1 = NewCalculator1()
print(NewCalculator1.__bases__)
calc1.calculate(“1 + 3 * 5”)
calc1.printResult()

注意:多继承会提高代码重用率,但也会增加代码复杂度。

8.2.7 接口
在很多面向对象语言中都有接口的概念。接口其实就是一个规范,指定了一个类中有哪些成员。接口也经常用在多态中,一个类可以有多个接口,也就是多个规范。不过python语言中并没有这些东西,在调用一个对象时,就假设这个方法在对象中存在。当然,更稳妥的方法就是在调用方法之前先使用hasattr函数检测一下,如果方法在对象中存在,该函数返回True,否则返回False。

还可以使用getattr函数实现同样的功能。该函数有三个参数,前2个参数与hasattr函数完全一样(对象、成员),当第2个参数指定的成员不存在时,getattr函数会返回第3个参数指定的默认值。

与getattr函数对应的是setattr函数,第3个参数是用来设置对象中成员的值。(有则更新,无则添加)

class MyClass:
def method1(self):
print(“method1”)
def default(self):
print(“default”)

my = MyClass()

# my对象中是否存在method1方法
if hasattr(my, ‘method1′):
my.method1()
else:
print(“method2方法不存在”)

# my对象中是否存在method2方法
if hasattr(my,’method2’):
my.method2()
else:
print(“method2方法不存在”)

# my对象中method2方法不存在,则返回”default”值
method = getattr(my, ‘method2’,my.default)
method()

# 对象中成员不存在,则添加成员
def method2():
print(“动态添加的method2”)
setattr(my, ‘method2’, method2)
my.method2()

python:String-模版替换操作

python:String-模版替换操作

capwords函数

将序列中的每个单词变成首字母大写

def capwords(s, sep=None):
“””capwords(s [,sep]) -> string
Split the argument into words using split, capitalize each
word using capitalize, and join the capitalized words using
join. If the optional second argument sep is absent or None,
runs of whitespace characters are replaced by a single space
and leading and trailing whitespace are removed, otherwise
sep is used to split and join the words.
“””
return (sep or ‘ ‘).join(x.capitalize() for x in s.split(sep))
从源码中我们在return处可以看到是先使用了split进行切割(默认走空格切割),然后在使用join进行结果的合并

栗子:

import string

s = ‘this is old old man’
print(s)

print(string.capwords(s))
运行后的结果如下

this is old old man
This Is Old Old Man

Process finished with exit code 0
从运行结果看,可以清晰的看出原本小写的单词,*后变成首字母大写了…

 

模版

模版拼接也是内置拼接的代替方法,使用string.Template拼接时,要在名字前加前缀$来标识变量(例如:$var),如果要和周围的变量区分的话,那就加一个花括号(例如:${var})。

栗子:

import string

values = {‘var’: 66}

t = string.Template(“””
variable : $var
escape : $var
variable in text : ${var}和我拼接
“””)
print(t.substitute(values))
print(t.safe_substitute(values))
运行后的结果如下

variable : 66
escape : 66
variable in text : 66和我拼接

variable : 66
escape : 66
variable in text : 66和我拼接
从上述结果来看,数据是已经替换成功了,唯一不好的就是数据都是字符型,如果参数的类型需要更换,就得再做一层处理… 具体怎么做可自行动手操作

 

substitute和safe_substitute方法

substitute处理替换时,存在就替换,不存在则抛KeyError错误

safe_substitute处理替换时,存在就替换,不存在就是原参数返回,不会抛错误

栗子:substitute方法

import string

values = {‘var’: 66}

t = string.Template(“””
variable : $var
escape : $var
missing : $missing
variable in text : ${var}和我拼接
“””)
print(t.substitute(values))
运行后的结果如下

Traceback (most recent call last):
File “/Users/lifeng/python-projects/Test/pythonScripts/python_string.py”, line 21, in <module>
print(t.substitute(values))
File “/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/string.py”, line 121, in substitute
return self.pattern.sub(convert, self.template)
File “/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/string.py”, line 114, in convert
return str(mapping[named])
KeyError: ‘missing’
从上述运行结果可以看出,报错是KeyError:’missing’

 

栗子:safe_substitute方法

import string

values = {‘var’: 66}

t = string.Template(“””
variable : $var
escape : $var
missing : $missing
variable in text : ${var}和我拼接
“””)
print(t.safe_substitute(values))
运行后的结果如下

variable : 66
escape : 66
missing : $missing
variable in text : 66和我拼接

Process finished with exit code 0
从上述运行结果看,没有参数替换的参数,就原参数返回,且不会报错

 

 

可能有些时候我们觉得$符号不舒服,想更换一种符号用,那这样我们就可以继承Template进行重写操作

class MyTemplate(string.Template):
delimiter = ‘!’

l = MyTemplate(“””
variable : !var
escape : !var
missing : $missing
variable in text : ${var}和我拼接
“””)
print(l.substitute(values))
在上述代码中我们只是把源码中的delimiter变量的值修改了,运行后的结果如下

上述代码中我们把定界符$符号修改成了!符号,参数替换也是成功的,当然你如果想重写别处代码也是可以的,因为Template这个类本身就有正则表达式,只要源码看得懂那就改起来吧…

 

 

JavaScript函数

JavaScript函数

JavaScript函数
文章目录
JavaScript函数
一、函数定义
二、函数参数
三、函数的返回值与表达式
函数返回值return
函数表达式
四、匿名函数
五、箭头函数
六、小结
七、函数全局变量与局部变量
1.介绍
2.示例
一、函数定义
用来封装你的重复性代码
在python定义函数需要用到关键字def
在 js 中使用 function 定义函数
语法:

// 语法
function [函数名](形参1,形参2,形参3…){
[函数体代码];
}

// 无参函数
function func1() {
console.log(‘hello world’);
}
func1(); // hello world

// 有参函数
function func2(a, b) {
console.log(a, b);
}
func2(1, 2); // 1 2
func2(1, 2, 3, 4, 5, 6, 7, 8, 9); // 1 2(多了没关系 只要对应的数据)
func2(1); // 1 undefined(少了也没关系, 未定义的会用undefined填补)

二、函数参数
参数一般五个以下
函数内引用的参数大于传入的参数会以 “undefined” 替代
函数内引用的参数小于传入的参数, 那么只会取出相应的参数
function cook(isBad,a,b,c){ // isbad形式参数
if (isBad){
alert(‘做饭’);

}else{
alert(‘点一个外卖’);
}
}
var bad = false; //刀坏了
cook(bad); // 点一个外卖

arguments 参数
函数中的arguments参数: 能够获取到函数接受到的所有的参数

function func3(a, b, c) {
console.log(arguments);
}
func3(1, 2, 3);
// 执行结果:
/*
Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
0: 1
1: 2
2: 3
*/

// 使用arguments判断传参的确切性
function func4(a, b) {
if (arguments.length < 2) {
console.log(‘参数传少了!’);
} else if (arguments.length > 2) {
console.log(‘参数传多了!’);
} else {
console.log(‘正常执行’);
}
}
func4(1, 2, 3); // 参数传多了!
func4(1); // 参数传少了!
func4(1, 2); // 正常执行

三、函数的返回值与表达式
函数返回值return
函数的返回值: 使用的也是等同于python中的关键字return
function func5() { // 返回一个值
return 666
}
console.log(func5()); // 666

function func6(a, b) { // 返回多个值, 多个值必须被包含不然就会返回逗号前面的那一个值.
return a, b
}
console.log(func6(1, 2)); // 2

function func7(a, b) {
return [a, b]
}
console.log(func7(1, 2)); // [1, 2]

函数表达式
var division = function(a,b){
return a/b;
}
// 调用
division(10,2) // 5

四、匿名函数
没有名字的函数
函数可以被赋值, 但对于一个匿名函数来说赋予名字没有意义
匿名函数一般与 map( ) 之类的函数组合使用
/*function () {
console.log(‘哈哈哈’);
}*/
let res = function() { // 函数还可以被赋值, 对于匿名函数这也赋值虽然没有什么意义, 但是为了然函数能正常执行, 还是赋值一个变量吧~
console.log(‘哈哈哈’);
};
res(); // 哈哈哈

五、箭头函数
箭头函数: 主要用来处理简单的业务逻辑 类似于python中的匿名函
ES6中允许使用“箭头”(=>)定义函数
// 如果箭头函数不需要参数或需要多个参数,就是用圆括号代表参数部分
var f = () => 5;

// 等同于
var f = function(){return 5};

let func8 = v => v*2; // 箭头左边的是形参 右边的是返回值
console.log(func8(2)); // 4

let func9 = function (v) { // 上面与下面作用等同
return v * 2;
};
console.log(func9(2)); // 4`

六、小结
/*
函数定义: 无参, 有参
函数调用: 无参直接调用. 有参调用传值多余部分不要, 少的部分用undefined提补.
arguments: 可以接受所有参数, 可以通过arguments.length获取传入的参数个数.
函数返回值: 单个返回值. 多个返回值需被包含, 如果不包含只返回逗号前面一个值.
匿名函数: 匿名函数可被赋值.
箭头函数:
无参: let a = () => 返回值;
有参: let a = 形参 => 返回值;
*/

七、函数全局变量与局部变量
1.介绍
局部变量:

在JavaScript函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它(该变量的作用域是函数内部)。只要函数运行完毕,本地变量就会被删除。
全局变量:

在函数外声明的变量是全局变量,网页上的所有脚本和函数都能访问它。
变量生存周期:

JavaScript变量的生命期从它们被声明的时间开始。
局部变量会在函数运行以后被删除。
全局变量会在页面关闭后被删除。
作用域

首先在函数内部查找变量,找不到则到外层函数查找,逐步找到*外层。与python作用域关系查找一模一样
2.示例
跟python查找变量的顺序一致

示例1
var city = “BeiJing”;
function f() {
var city = “ShangHai”;
function inner(){
var city = “ShenZhen”;
console.log(city);
}
inner();
}
f(); // 输出结果是? ShenZhen

// ES6推荐使用let
let city = “BeiJing”;
function f() {
let city = “ShangHai”;
function inner(){
let city = “ShenZhen”;
console.log(city);
}
inner();
}
f(); // 输出结果是? ShenZhen

示例2
var city = “BeiJing”;
function Bar() {
console.log(city);
}
function f() {
var city = “ShangHai”;
return Bar;
}
var ret = f();
ret(); // 打印结果是? BeiJing

// ES6推荐使用let
let city = “BeiJing”;
function Bar() {
console.log(city);
}
function f() {
let city = “ShangHai”;
return Bar;
}
let ret = f();
ret(); // 打印结果是? BeiJing

示例3:闭包函数
var city = “BeiJing”;
function f(){
var city = “ShangHai”;
function inner(){
console.log(city);
}
return inner;
}
var ret = f();
ret(); // ShangHai

// ES6推荐使用let
let city = “BeiJing”;
function f(){
let city = “ShangHai”;
function inner(){
console.log(city);
}
return inner;
}
let ret = f();
ret(); // ShangHai

探寻Python类的鼻祖——元类

探寻Python类的鼻祖——元类

Python中万物皆对象
Python是一门面向对象的语言,所以Python中数字、字符串、列表、集合、字典、函数、类等都是对象。

利用 type() 来查看Python中的各对象类型

In [11]: # 数字

In [12]: type(10)
Out[12]: int

In [13]: type(3.1415926)
Out[13]: float

In [14]: # 字符串

In [15]: type(‘a’)
Out[15]: str

In [16]: type(“abc”)
Out[16]: str

In [17]: # 列表

In [18]: type(list)
Out[18]: type

In [19]: type([])
Out[19]: list

In [20]: # 集合

In [21]: type(set)
Out[21]: type

In [22]: my_set = {1, 2, 3}

In [23]: type(my_set)
Out[23]: set

In [24]: # 字典

In [25]: type(dict)
Out[25]: type

In [26]: my_dict = {‘name’: ‘hui’}

In [27]: type(my_dict)
Out[27]: dict

In [28]: # 函数

In [29]: def func():
…: pass
…:

In [30]: type(func)
Out[30]: function

In [31]: # 类

In [32]: class Foo(object):
…: pass
…:

In [33]: type(Foo)
Out[33]: type

In [34]: f = Foo()

In [35]: type(f)
Out[35]: __main__.Foo

In [36]: # type

In [37]: type(type)
Out[37]: type

可以看出

数字 1 是 int类型 的对象
字符串 abc 是 str类型 的对象
列表、集合、字典是 type类型 的对象,其创建出来的对象才分别属于 list、set、dict 类型
函数 func 是 function类型 的对象
自定义类 Foo 创建出来的对象 f 是 Foo 类型,其类本身 Foo 则是 type类型 的对象。
连 type 本身都是type类型的对象

1. 类也是对象
类就是拥有相等功能和相同的属性的对象的集合

在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段。在 Python 中这一点仍然成立:

In [1]: class ObjectCreator(object):
…: pass
…:

In [2]: my_object = ObjectCreator()

In [3]: print(my_object)
<__main__.ObjectCreator object at 0x0000021257B5A248>

但是,Python中的类还远不止如此。类同样也是一种对象。是的,没错,就是对象。只要你 使用关键字 class,Python解释器在执行的时候就会创建一个对象。

下面的代码段:

>>> class ObjectCreator(object):
… pass

将在内存中创建一个对象,名字就是 ObjectCreator。这个 对象(类对象ObjectCreator)拥有创建对象(实例对象)的能力。但是,它的本质仍然是一个对象,于是乎你可以对它做如下的操作:

你可以将它赋值给一个变量
你可以拷贝它
你可以为它增加属性
你可以将它作为函数参数进行传递
如下示例:

In [39]: class ObjectCreator(object):
…: pass
…:

In [40]: print(ObjectCreator)
<class ‘__main__.ObjectCreator’>

In [41]:# 当作参数传递

In [41]: def out(obj):
…: print(obj)
…:

In [42]: out(ObjectCreator)
<class ‘__main__.ObjectCreator’>

In [43]: # hasattr 判断一个类是否有某种属性

In [44]: hasattr(ObjectCreator, ‘name’)
Out[44]: False

In [45]: # 新增类属性

In [46]: ObjectCreator.name = ‘hui’

In [47]: hasattr(ObjectCreator, ‘name’)
Out[47]: True

In [48]: ObjectCreator.name
Out[48]: ‘hui’

In [49]: # 将类赋值给变量

In [50]: obj = ObjectCreator

In [51]: obj()
Out[51]: <__main__.ObjectCreator at 0x212596a7248>

In [52]:

2. 动态地创建类
因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。首先,你可以在函数中创建类,使用 class 关键字即可。

def cls_factory(cls_name):
“””
创建类工厂
:param: cls_name 创建类的名称
“””
if cls_name == ‘Foo’:
class Foo():
pass
return Foo # 返回的是类,不是类的实例

elif cls_name == ‘Bar’:
class Bar():
pass
return Bar

IPython 测验

MyClass = cls_factory(‘Foo’)

In [60]: MyClass
Out[60]: __main__.cls_factory.<locals>.Foo # 函数返回的是类,不是类的实例

In [61]: MyClass()
Out[61]: <__main__.cls_factory.<locals>.Foo at 0x21258b1a9c8>

但这还不够动态,因为你仍然需要自己编写整个类的代码。由于类也是对象,所以它们必须是通过什么东西来生成的才对。

当你使用class关键字时,Python解释器自动创建这个对象。但就和Python中的大多数事情一样,Python仍然提供给你手动处理的方法。

3. 使用 type 创建类
type 还有一种完全不同的功能,动态的创建类。

type可以接受一个类的描述作为参数,然后返回一个类。(要知道,根据传入参数的不同,同一个函数拥有两种完全不同的用法是一件很傻的事情,但这在Python中是为了保持向后兼容性)

type 可以像这样工作:

type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

比如下面的代码:

In [63]: class Test:
…: pass
…:

In [64]: Test()
Out[64]: <__main__.Test at 0x21258b34048>

In [65]:

可以手动像这样创建:

In [69]:# 使用type定义类

In [69]: Test2 = type(‘Test2’, (), {})

In [70]: Test2()
Out[70]: <__main__.Test2 at 0x21259665808>

我们使用 Test2 作为类名,并且也可以把它当做一个变量来作为类的引用。类和变量是不同的,这里没有任何理由把事情弄的复杂。即 type函数 中第1个实参,也可以叫做其他的名字,这个名字表示类的名字

In [71]: UserCls = type(‘User’, (), {})

In [72]: print(UserCls)
<class ‘__main__.User’>

In [73]:

使用 help 来测试这2个类

In [74]: # 用 help 查看 Test类

In [75]: help(Test)
Help on class Test in module __main__:

class Test(builtins.object)
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)

In [76]: # 用 help 查看 Test2类

In [77]: help(Test2)
Help on class Test2 in module __main__:

class Test2(builtins.object)
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)

In [78]:

4. 使用type创建带有属性的类
type 接受一个字典来为类定义属性,因此

Parent = type(‘Parent’, (), {‘name’: ‘hui’})
1
可以翻译为:

class Parent(object):
name = ‘hui’
1
2
并且可以将 Parent 当成一个普通的类一样使用:

In [79]: Parent = type(‘Parent’, (), {‘name’: ‘hui’})

In [80]: print(Parent)
<class ‘__main__.Parent’>

In [81]: Parent.name
Out[81]: ‘hui’

In [82]: p = Parent()

In [83]: p.name
Out[83]: ‘hui’

当然,你可以继承这个类,代码如下:

class Child1(Parent):
name = ‘jack’
sex = ‘男’

class Child2(Parent):
name = ‘mary’
sex = ‘女’

就可以写成:

Child1 = type(‘Child1’, (Parent, ), {‘name’: ‘jack’, ‘sex’: ‘男’})

In [85]: Child2 = type(‘Child2’, (Parent, ), {‘name’: ‘mary’, ‘sex’: ‘女’})

In [87]: Child1.name, Child1.sex
Out[87]: (‘jack’, ‘男’)

In [88]: Child2.name, Child2.sex
Out[88]: (‘mary’, ‘女’)

注意:

type 的第2个参数,元组中是父类的名字,而不是字符串
添加的属性是 类属性,并不是实例属性

5. 使用type创建带有方法的类
*终你会希望为你的类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值就可以了。

添加实例方法
In [89]: Parent = type(‘Parent’, (), {‘name’: ‘hui’})

In [90]: # 定义函数

In [91]: def get_name(self):
…: return self.name
…:

In [92]: Child3 = type(‘Child3’, (Parent, ), {‘name’: ‘blob’, ‘get_name’: get_name})

In [93]: c3 = Child3()

In [94]: c3.name
Out[94]: ‘blob’

In [95]: c3.get_name()
Out[95]: ‘blob’

添加静态方法
In [96]: Parent = type(‘Parent’, (), {‘name’: ‘hui’})

In [97]: # 定义静态方法

In [98]: @staticmethod
…: def test_static():
…: print(‘static method called…’)
…:

In [100]: Child4 = type(‘Child4’, (Parent, ), {‘name’: ‘zhangsan’, ‘test_static’: test_static})

In [101]: c4 = Child4()

In [102]: c4.test_static()
static method called…

In [103]: Child4.test_static()
static method called…

添加类方法
In [105]: Parent = type(‘Parent’, (), {‘name’: ‘hui’})

In [106]: # 定义类方法

In [107]: @classmethod
…: def test_class(cls):
…: print(cls.name)
…:

In [108]: Child5 = type(‘Child5’, (Parent, ), {‘name’: ‘lisi’, ‘test_class’: test_class})

In [109]: c5 = Child5()

In [110]: c5.test_class()
lisi

In [111]: Child5.test_class()
lisi

你可以看到,在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字 class 时 Python 在幕后做的事情,就是通过元类来实现的。

较为完整的使用 type 创建类的方式:

class Animal(object):

def eat(self):
print(‘吃东西’)

def dog_eat(self):
print(‘喜欢吃骨头’)

def cat_eat(self):
print(‘喜欢吃鱼’)

Dog = type(‘Dog’, (Animal, ), {‘tyep’: ‘哺乳类’, ‘eat’: dog_eat})

Cat = type(‘Cat’, (Animal, ), {‘tyep’: ‘哺乳类’, ‘eat’: cat_eat})

# ipython 测验
In [125]: animal = Animal()

In [126]: dog = Dog()

In [127]: cat = Cat()

In [128]: animal.eat()
吃东西

In [129]: dog.eat()
喜欢吃骨头

In [130]: cat.eat()
喜欢吃鱼

 

6. 到底什么是元类(终于到主题了)
元类就是用来创建类的【东西】。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。

元类就是用来创建这些类(对象)的,元类就是类的类,你可以这样理解为:

MyClass = MetaClass() # 使用元类创建出一个对象,这个对象称为“类”
my_object = MyClass() # 使用“类”来创建出实例对象
1
2
你已经看到了type可以让你像这样做:

MyClass = type(‘MyClass’, (), {})
1
这是因为函数 type 实际上是一个元类。type 就是 Python在背后用来创建所有类的元类。现在你想知道那为什么 type 会全部采用小写形式而不是 Type 呢?好吧,我猜这是为了和 str 保持一致性,str是用来创建字符串对象的类,而 int 是用来创建整数对象的类。type 就是创建类对象的类。你可以通过检查 __class__ 属性来看到这一点。因此 Python中万物皆对象

现在,对于任何一个 __class__ 的 __class__ 属性又是什么呢?

In [136]: a = 10

In [137]: b = ‘acb’

In [138]: li = [1, 2, 3]

In [139]: a.__class__.__class__
Out[139]: type

In [140]: b.__class__.__class__
Out[140]: type

In [141]: li.__class__.__class__
Out[141]: type

In [142]: li.__class__.__class__.__class__
Out[142]: type

因此,元类就是创建类这种对象的东西。type 就是 Python的内建元类,当然了,你也可以创建自己的元类。

7. __metaclass__ 属性
你可以在定义一个类的时候为其添加 __metaclass__ 属性。

class Foo(object):
__metaclass__ = something…
…省略…

如果你这么做了,Python就会用元类来创建类Foo。小心点,这里面有些技巧。你首先写下 class Foo(object),但是类Foo还没有在内存中创建。Python会在类的定义中寻找 __metaclass__ 属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的 type 来创建这个类。

class Foo(Bar):
pass

Python做了如下的操作:

Foo中有 __metaclass__ 这个属性吗?如果有,Python会通过 __metaclass__ 创建一个名字为Foo的类(对象)
如果Python没有找到 __metaclass__,它会继续在 Bar(父类) 中寻找 __metaclass__ 属性,并尝试做和前面同样的操作。
如果Python在任何父类中都找不到 __metaclass__,它就会在模块层次中去寻找 __metaclass__,并尝试做同样的操作。
如果还是找不到 __metaclass__ ,Python就会用内置的 type 来创建这个类对象。
现在的问题就是,你可以在 __metaclass__ 中放置些什么代码呢?

答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化的type都可以。

8. 自定义元类
元类的主要目的就是为了当创建类时能够自动地改变类。

假想一个很傻的例子,你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到,但其中一种就是通过在模块级别设定 __metaclass__。采用这种方法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。

幸运的是,__metaclass__ 实际上可以被任意调用,它并不需要是一个正式的类。所以,我们这里就先以一个简单的函数作为例子开始。

python2中
# -*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):

# class_name 会保存类的名字 Foo
# class_parents 会保存类的父类 object
# class_attr 会以字典的方式保存所有的类属性

# 遍历属性字典,把不是__开头的属性名字变为大写
new_attr = {}
for name, value in class_attr.items():
if not name.startswith(“__”):
new_attr[name.upper()] = value

# 调用type来创建一个类
return type(class_name, class_parents, new_attr)

class Foo(object):
__metaclass__ = upper_attr # 设置Foo类的元类为upper_attr
bar = ‘bip’

print(hasattr(Foo, ‘bar’))
# Flase
print(hasattr(Foo, ‘BAR’))
# True

f = Foo()
print(f.BAR)

python3中
# -*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):

#遍历属性字典,把不是__开头的属性名字变为大写
new_attr = {}
for name,value in class_attr.items():
if not name.startswith(“__”):
new_attr[name.upper()] = value

#调用type来创建一个类
return type(class_name, class_parents, new_attr)

# 再类的继承()中使用metaclass
class Foo(object, metaclass=upper_attr):
bar = ‘bip’

print(hasattr(Foo, ‘bar’))
# Flase
print(hasattr(Foo, ‘BAR’))
# True

f = Foo()
print(f.BAR)

再做一次,这一次用一个真正的 class 来当做元类。

class UpperAttrMetaClass(type):

def __new__(cls, class_name, class_parents, class_attr):
# 遍历属性字典,把不是__开头的属性名字变为大写
new_attr = {}
for name, value in class_attr.items():
if not name.startswith(“__”):
new_attr[name.upper()] = value

# 方法1:通过’type’来做类对象的创建
return type(class_name, class_parents, new_attr)

# 方法2:复用type.__new__方法
# 这就是基本的OOP编程,没什么魔法
# return type.__new__(cls, class_name, class_parents, new_attr)

# python3的用法
class Foo(object, metaclass=UpperAttrMetaClass):
bar = ‘bip’

# python2的用法
class Foo(object):
__metaclass__ = UpperAttrMetaClass
bar = ‘bip’

print(hasattr(Foo, ‘bar’))
# 输出: False
print(hasattr(Foo, ‘BAR’))
# 输出: True

f = Foo()
print(f.BAR)
# 输出: ‘bip’

__new__ 是在__init__之前被调用的特殊方法
__new__是用来创建对象并返回之的方法
而__init__只是用来将传入的参数初始化给对象
这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__

就是这样,除此之外,关于元类真的没有别的可说的了。但就元类本身而言,它们其实是很简单的:

拦截类的创建
修改类
返回修改之后的类

究竟为什么要使用元类?
现在回到我们的大主题上来,究竟是为什么你会去使用这样一种容易出错且晦涩的特性?

好吧,一般来说,你根本就用不上它:

“元类就是深度的魔法,99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类,那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么,而且根本不需要解释为什么要用元类。” —— Python界的领袖 Tim Peters

 

第十二届蓝桥杯填空题

第十二届蓝桥杯填空题

版权
目录

空间

卡片

直线

路径

空间
问题描述:

小蓝准备用 256MB 的内存空间开一个数组,数组的每个元素都是 32 位二进制整数,如果不考虑程序占用的空间和维护内存需要的辅助空间,请问256MB 的空间可以存储多少个 32 位二进制整数?

思路分析:

先将MB转换为字节Byte,也就是Byte(B),1MB = 1024KB, 1KB = 1024B,1B = 8bit(位)所以256 MB = 256 * 1024 * 1024B,32位二进制整数的存储空间也即32bit = 32 / 8 = 4B,所以可以存在 256 * 1024 * 1024 / 4,使用程序计算出结果为:67108864

if __name__ == ‘__main__’:
print(256 * 1024 * 1024 // 4)
卡片
问题描述:小蓝有很多数字卡片,每张卡片上都是数字 0 到 9。小蓝准备用这些卡片来拼一些数,他想从 1 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。小蓝想知道自己能从 1 拼到多少。例如,当小蓝有 30 张卡片,其中 0 到 9 各 3 张,则小蓝可以拼出 1 到 10,但是拼 11 时卡片 1 已经只有一张了,不够拼出 11。现在小蓝手里有 0 到 9 的卡片各 2021 张,共 20210 张,请问小蓝可以从1拼到多少?提示:建议使用计算机编程解决问题。

思路分析:

分析题目可以知道模拟整个过程即可,我们可以使用一个循环,只要可以拼出当前的数字i那么循环继续,否则break输出i – 1(本来是送分题在比赛的时候不知道啥情况模拟这个过程还是算错了),使用一个方法check来检查当前数字i是否可以拼出即可,使用check方法来检查会比较保险,我可能在一开始的时候没有使用方法来检查导致所有代码写在一起错了。答案为3181。

cards = [2021] * 10

# 检查当前的数字n是否是拼出来
def check(n: int):
while n:
t = n % 10
if cards[t] – 1 < 0: return False
cards[t] -= 1
n //= 10
return True

if __name__ == ‘__main__’:
i = 1
while True:
if check(i):
i += 1
else:
print(i – 1)
break
直线
问题描述:

在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。给定平面上 2 × 3 个整点 {(x,y)|0 ≤ x < 2; 0 ≤ y < 3; ,即横坐标是 0 到 1 (包含 0 和 1) 之间的整数、纵坐标是 0 到 2 (包含 0 和 2) 之间的整数的点。这些点一共确定了 11 条不同的直线。给定平面上 20 × 21 个整点 {(x,y)|0 ≤ x < 20; 0 ≤ y < 21; ,即横坐标是 0 到 19 (包含 0 和 19) 之间的整数、纵坐标是 0 到 20 (包含 0 和 20) 之间的整数的点。请问这些点一共确定了多少条不同的直线?

思路分析:

一开始的是没有想出来,其实可以枚举所有可能的点的坐标,计算两点确定的直线斜率k与截距b,因为使用的是python语言,所以可以将k,b作为元组(k,b确定的就是一条直线)存储到列表中,然后对列表进行排序,先对斜率排序,斜率相同对截距排序,因为浮点数在存储的时候是有误差的所以需要判断两条直线的斜率或者截距大于1e-8那么判定为不同的直线。答案为:40257

if __name__ == ‘__main__’:
# 使用列表中元组作为元素这样方便后面方便排序
lines = list()
for x1 in range(20):
for y1 in range(21):
for x2 in range(20):
for y2 in range(21):
# x1 == x2的时候斜率不存在
if x1 != x2:
k = (y2 – y1) / (x2 – x1)
b = y1 – k * x1
lines.append((k, b))
# lambda表达式规定先对元组的*个元素斜率k排序, k相同那么则对b排序
lines.sort(key=lambda x: (x[0], x[1]))
# 循环中是从第二条直线开始的
res = 1
for i in range(1, len(lines)):
# 误差大于1e-8判定为不是同一条直线
if lines[i][0] – lines[i – 1][0] > 10 ** -8 or lines[i][1] – lines[i – 1][1] > 10 ** -8:
res += 1
# 加上斜率不存在的20条直线
print(res + 20)
路径
问题描述:

小蓝学习了*短路径之后特别高兴,他定义了一个特别的图,希望找到图中的*短路径。小蓝的图由 2021 个结点组成,依次编号 1 至 2021。对于两个不同的结点 a, b,如果 a 和 b 的差的*对值大于 21,则两个结点之间没有边相连;如果 a 和 b 的差的*对值小于等于 21,则两个点之间有一条长度为 a 和 b 的*小公倍数的无向边相连。例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。请计算,结点 1 和结点 2021 之间的*短路径长度是多少。
提示:建议使用计算机编程解决问题。

思路分析:

① 分析题目可以知道我们就可以将节点的边看成是有向边,我们要求解的是从起点1到终点2021的*短路径,所以本质上求解的是*短路径,使用dijkstra算法或者是spfa算法都可以求解,下面使用spfa算法求解单源*短路径,可以知道题目没有任何限制条件所以直接使用spfa算法模板求解接即可(*短路径的裸题)。首先需要创建图,因为使用的是python语言所以使用字典来构建图,键表示顶点,值为列表类型,将顶点能够到达的终点以及对应的权重作为元组的元素添加到字典键对应的列表中即可。创建图之后接下来使用spfa算法的模板即可。

import collections
import sys

# 求解*大公约数
def gcd(a: int, b: int):
while b:
a, b = b, a % b
return a

if __name__ == ‘__main__’:
# 使用spfa单源路径进行求解
# 首先是建图, 看成是有向图
graph = collections.defaultdict(list)
n = 2100
for i in range(1, 2022):
for j in range(i + 1, 2022):
if abs(i – j) <= 21:
# 权重为两个顶点的*小公倍数, 即为两个数相乘除以他们的*大公约数
w = i * j // gcd(i, j)
# 构建当前顶点对应的终点以及边的权重
graph[i].append((j, w))
queue = collections.deque()
s = 1
queue.append(s)
dis = [sys.maxsize] * (n + 1)
dis[s] = 0
used = [0] * (n + 1)
used[s] = 1
while queue:
cur = queue.popleft()
used[cur] = 0
for next in graph[cur]:
if dis[cur] + next[1] < dis[next[0]]:
dis[next[0]] = dis[cur] + next[1]
if used[next[0]] == 0:
used[next[0]] = 1
queue.append(next[0])
print(dis[2021])
② 上面的*短路径还是很容易看出来的,而且题目中求解的问题就是*短路径,除了*短路径算法我们还可以使用动态规划的思路求解。这道题目有点类似于多叉树,从起点出发有很多条路径到达下一个顶点,我们每到达一个顶点那么计算从之前的顶点到达当前顶点的*短路径从而每一次都更新节点的*短距离,这样通过一步步的递推一直到第2021个顶点。

import sys

# 求解*大公约数
def gcd(a: int, b: int):
while b:
a, b = b, a % b
return a

if __name__ == ‘__main__’:
dis = [sys.maxsize] * 2100
dis[1] = 0
for i in range(2, 2022):
for j in range(1, i):
if abs(i – j) <= 21:
# 看成是有向边, 从当前节点之前的节点来更新到达当前节点的*短路径
dis[i] = min(dis[i], dis[j] + i * j // gcd(i, j))
print(dis[2021])

iOS中数组遍历的方法及比较

数组遍历是编码中很常见的一种需求,我们来扒一拔iOS里面都有什么样的方法来实现,有什么特点。

因为iOS是兼容C语言的,所以C语言里面的**常见的for循环遍历是没有问题的。

本文中用的数组是获取的系统的语言数组,大约有30多个数据,虽然还不够模拟大批量的数据,但对于方法的验证是没有问题的了。

NSArray *langArray = [[NSUserDefaults standardUserDefaults] arrayForKey:@”AppleLanguages”];
*种方法是**熟悉的C语言演化过来的:

for (int i = 0; i<langArray.count; i++) {
NSLog(@”langArray[%d]=%@”, i, langArray[i]);
}
这个方法*普通,效率也一般,但它也有好处,一是方便针对下标的处理,就是说如果我要处理i==10的情况是很简便的,另一个是可以比较方便的反向遍历。

Objective-C 1.0里面的NSEnumerator也可以进行遍历,代码如下:

NSEnumerator *enumerator = [langArray objectEnumerator];
id object;
while ((object = [enumerator nextObject]) != nil) {
NSLog(@”langArray=%@”, object);
}
这里我们可以看到没有下标了,通过nextObject的方法来遍历。这个方法的好处是对于遍历NSDictionary和NSSet代码也比较类似,不便的是对于下标的处理会不方便,另外反向遍历需要用reverseObjectEnumerator方法。

Objective-C发展到2.0时又有了快速遍历功能,代码如下:

for (id object in langArray) {
NSLog(@”langArray=%@”, object);
}
这里代码简洁清晰,很长时间是我写代码的首选,号称效率也*高,不过不便之处同样明显,如果算法要求知道数组的下标,这个方法就抓瞎了。另外,反向需要通过[langArray reverseObjectEnumerator]来实现。

等到block出来后,iOS里面新增加了enumerateObjectsUsingBlock:的方法,代码如下:

[langArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@”idx=%d, id=%@”, idx, obj);
}];
这里我们看到block里面的参数包括object,下标以及是否停止遍历,应该说,这个能满足基本所有的遍历需求了,有下标,有运行的对象,还有是否继续遍历的标志。不过反向遍历呢?苹果提供了另外一个方法:

[langArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSLog(@”idx=%d, id=%@”, idx, obj);
}];
这个enumerateObjectsWithOptions:usingBlock:方法比enumerateObjectsUsingBlock:方法多传了一个参数,这个参数指定了遍历的顺序。

说到这里,如果我们选择正向遍历,那么这两种方法是一样的么?答案也是否定的。在enumerateObjectsWithOptions:usingBlock:方法里面,如果指定了NSEnumerationConcurrent顺序,那么底层通过GCD来处理并发执行事宜,具体实现可能会用到dispatch group。也就是说,这个会用多线程来并发实现,并不保证按照顺序执行,但效率肯定是杠杠的!

我们来看一下打印结果:

2014-06-17 15:46:44.413 testStoryboard[2703:3503] idx=32, id=hu
2014-06-17 15:46:44.413 testStoryboard[2703:1303] idx=16, id=ru
2014-06-17 15:46:44.416 testStoryboard[2703:3503] idx=33, id=vi
2014-06-17 15:46:44.412 testStoryboard[2703:60b] idx=0, id=zh-Hant
2014-06-17 15:46:44.417 testStoryboard[2703:1303] idx=17, id=pl
2014-06-17 15:46:44.417 testStoryboard[2703:60b] idx=1, id=zh-Hans
2014-06-17 15:46:44.417 testStoryboard[2703:1303] idx=18, id=tr
2014-06-17 15:46:44.419 testStoryboard[2703:60b] idx=2, id=en
2014-06-17 15:46:44.419 testStoryboard[2703:1303] idx=19, id=uk
2014-06-17 15:46:44.421 testStoryboard[2703:60b] idx=3, id=fr
2014-06-17 15:46:44.421 testStoryboard[2703:1303] idx=20, id=ar
2014-06-17 15:46:44.421 testStoryboard[2703:60b] idx=4, id=de
2014-06-17 15:46:44.422 testStoryboard[2703:60b] idx=5, id=ja
2014-06-17 15:46:44.422 testStoryboard[2703:60b] idx=6, id=nl
2014-06-17 15:46:44.421 testStoryboard[2703:1303] idx=21, id=hr
2014-06-17 15:46:44.423 testStoryboard[2703:60b] idx=7, id=it
2014-06-17 15:46:44.423 testStoryboard[2703:1303] idx=22, id=cs
2014-06-17 15:46:44.423 testStoryboard[2703:60b] idx=8, id=es
2014-06-17 15:46:44.424 testStoryboard[2703:1303] idx=23, id=el
2014-06-17 15:46:44.424 testStoryboard[2703:60b] idx=9, id=ko
2014-06-17 15:46:44.424 testStoryboard[2703:1303] idx=24, id=he
2014-06-17 15:46:44.425 testStoryboard[2703:60b] idx=10, id=pt
2014-06-17 15:46:44.425 testStoryboard[2703:60b] idx=11, id=pt-PT
2014-06-17 15:46:44.425 testStoryboard[2703:1303] idx=25, id=ro
2014-06-17 15:46:44.426 testStoryboard[2703:60b] idx=12, id=da
2014-06-17 15:46:44.426 testStoryboard[2703:1303] idx=26, id=sk
2014-06-17 15:46:44.426 testStoryboard[2703:60b] idx=13, id=fi
2014-06-17 15:46:44.426 testStoryboard[2703:1303] idx=27, id=th
2014-06-17 15:46:44.427 testStoryboard[2703:60b] idx=14, id=nb
2014-06-17 15:46:44.427 testStoryboard[2703:1303] idx=28, id=id
2014-06-17 15:46:44.428 testStoryboard[2703:60b] idx=15, id=sv
2014-06-17 15:46:44.428 testStoryboard[2703:1303] idx=29, id=ms
2014-06-17 15:46:44.429 testStoryboard[2703:1303] idx=30, id=en-GB
2014-06-17 15:46:44.429 testStoryboard[2703:1303] idx=31, id=ca
从这个结果我们可以看出,确实遍历了整个数组,但并发按照顺序从头到尾——也就是说,用到了dispatch group。这在遍历大数组而有相互独立时对于效率的提高是相当有利的,赞一个!

在iOS中,除数组外,还有NSDictionary和NSSet数据也是称为collection数据的,遍历有类似的地方,不过遍历没有数组那么频繁,方法上是差不多的。

 

iOS历史回顾(iOS1~iOS8)

苹果在9月推出了iOS8,推出了iPhone6和iPhone6+,想想我还看到身边有朋友在用iPhone1呢,当然,升级不到这么高版本了,但用的居然还是好好的,不得不说苹果的手机质量还是可圈可点的。

下面列出历史上的图:

%title插图%num

%title插图%num

%title插图%num

我们可以看到,苹果实际上每年都在推陈出新,iPhone的ID一直有变化,尺寸变薄,屏幕变大等等,也是市场的走向。但iOS软件UI的设计,实际上理念在一开始就确定了,后续有所变化,尤其是iOS7的扁平化,但还是谈不到突破旧天地。对*早的iOS1来说,Idle界面和主菜单界面合二为一,全触摸操作,块状图标。。。确确实实是伟大的作品。

UI的变迁实际上是苹果精益求精的过程,我们看几个自带应用的图标吧:

%title插图%num

%title插图%num

%title插图%num

这里我们可以看到,苹果每次发布实际都有微调,一开始是走拟物化,这条路走到巅峰之后推出了扁平化。扁平化一直很有争议,是好是坏我也说不上来,个人不是很喜欢,但确实感觉变朴素了一点。

图标本身有一定延续性,整体来说提示性比较清晰而且尽量避免使用文字。

iPhone到现在追求ID还是希望走到*致,iPhone6看到的参数,觉得做这么薄意义不是很大。看到消息说有人发现iPhone6容易弯,其实iPhone5就有这个问题。这么薄的手机,里面还要有很多器件,凭借苹果精湛的工业设计做了出来,但估计器件选型,内部散热,干涉等等都要做很多让步,而我个人实在不觉得多厚一点点销量会大幅下降——多少人都是冲着苹果超酷的体验,完善的软件生态链来的,冲着这么薄的ID似乎没怎么听说。Anyway,这个和主题无关。

 

2021年一季度全国规模以上文化及相关产业企业营业收入增长40.4% 两年平均增长10.0%

   据对全国6.3万家规模以上文化及相关产业企业调查,2021年一季度,上述企业实现营业收入25498亿元,按可比口径计算(详见附注6),比上年同期增长40.4%[1];比2019年一季度增长20.9%,两年平均增长10.0%。   分业态看,文化新业态特征较为明显的16个行业小类[2]实现营业收入8461亿元,比上年同期增长39.8%;比2019年一季度增长61.5%,两年平均增长27.1%。   分行业类别看,新闻信息服务营业收入2972亿元,比上年同期增长34.9%;内容创作生产5319亿元,增长32.3%;创意设计服务4266亿元,增长45.0%;文化传播渠道3007亿元,增长50.0%;文化投资运营102亿元,增长34.2%;文化娱乐休闲服务233亿元,增长101.8%;文化辅助生产和中介服务3435亿元,增长35.9%;文化装备生产1362亿元,增长37.4%;文化消费终端生产4803亿元,增长46.2%。   分产业类型看,文化制造业营业收入9124亿元,比上年同期增长36.0%;文化批发和零售业4383亿元,增长51.7%;文化服务业11991亿元,增长39.9%。   分领域看,文化核心领域[3]营业收入15899亿元,比上年同期增长39.9%;文化相关领域9599亿元,增长41.1%。 分区域看,东部地区实现营业收入19690亿元,比上年同期增长41.1%;中部地区3333亿元,增长39.5%;西部地区2245亿元,增长35.1%;东北地区230亿元,增长40.1%。   注:   [1] 为名义增长,未扣除价格因素。   [2] 新业态特征明显的16个行业小类是:广播电视集成播控,互联网搜索服务,互联网其他信息服务,数字出版,其他文化艺术业,动漫、游戏数字内容服务,互联网游戏服务,多媒体、游戏动漫和数字出版软件开发,增值电信文化服务,其他文化数字内容服务,互联网广告服务,互联网文化娱乐平台,版权和文化软件服务,娱乐用智能无人飞行器制造,可穿戴智能文化设备制造,其他智能文化消费设备制造。   [3] 文化核心领域包括新闻信息服务、内容创作生产、创意设计服务、文化传播渠道、文化投资运营、文化娱乐休闲服务6个行业,文化相关领域包括文化辅助生产和中介服务、文化装备生产、文化消费终端生产3个行业。 2021年一季度全国规模以上文化及相关产业企业营业收入情况    *对额 (亿元) 所占比重 (%) 比上年同期增长 (%) 比2019年一季度增长 (%) 两年平均增长 (%) 总 计 25498 100.0 40.4 20.9 10.0             按行业类别分            新闻信息服务 2972 11.7 34.9 50.6 22.7  内容创作生产 5319 20.9 32.3 22.1 10.5  创意设计服务 4266 16.7 45.0 41.4 18.9  文化传播渠道 3007 11.8 50.0 2.6 1.3  文化投资运营 102 0.4 34.2 20.7 9.9  文化娱乐休闲服务 233 0.9 101.8 -17.5 -9.2  文化辅助生产和中介服务 3435 13.5 35.9 6.4 3.1  文化装备生产 1362 5.3 37.4 10.2 5.0  文化消费终端生产 4803 18.8 46.2 24.2 11.4             按产业类型分            文化制造业 9124 35.8 36.0 10.9 5.3  文化批发和零售业 4383 17.2 51.7 10.3 5.0  文化服务业 11991 47.0 39.9 35.9 16.6             按领域分            文化核心领域 15899 62.4 39.9 25.0 11.8  文化相关领域 9599 37.6 41.1 15.2 7.3             按区域分            东部地区 19690 77.2 41.1 21.5 10.2  中部地区 3333 13.1 39.5 18.1 8.7  西部地区 2245 8.8 35.1 22.2 10.5  东北地区 230 0.9 40.1 5.4 2.7 注: 1.表中速度均为未扣除价格因素的名义增速。 2.表中部分数据因四舍五入,存在总计与分项合计不等的情况。    附注   1、指标解释   文化及相关产业指为社会公众提供文化产品和文化相关产品的生产活动的集合。《文化及相关产业分类(2018)》规定文化及相关产业包括新闻信息服务、内容创作生产、创意设计服务、文化传播渠道、文化投资运营、文化娱乐休闲服务、文化辅助生产和中介服务、文化装备生产、文化消费终端生产9个大类。   营业收入指“主营业务收入”及“其他业务收入”之和。   两年平均增速是指以2019年相应同期数为基数,采用几何平均的方法计算的增速。   2、统计范围   规模以上文化及相关产业的统计范围为《文化及相关产业分类(2018)》所规定行业范围内,年主营业务收入在2000万元及以上的工业企业;年主营业务收入在2000万元及以上的批发企业或年主营业务收入在500万元及以上的零售企业;年营业收入在1000万元及以上的服务业企业,其中交通运输、仓储和邮政业,信息传输、软件和信息技术服务业,水利、环境和公共设施管理业的年营业收入在2000万元及以上,居民服务、修理和其他服务业,文化、体育和娱乐业的年营业收入在500万元及以上。   3、调查方法   每季度进行规模以上文化及相关产业法人单位认定,根据国家统计联网直报平台上规模以上工业企业、限额以上批发和零售业企业、规模以上服务业企业全面调查取得的数据进行汇总。   4、行业分类标准   执行《文化及相关产业分类(2018)》,具体请参见http://www.stats.gov.cn/tjgz/tzgb/201804/t20180423_1595390.html。   5、东、中、西部和东北地区   东部地区包括北京、天津、河北、上海、江苏、浙江、福建、山东、广东、海南10个省(市);中部地区包括山西、安徽、江西、河南、湖北、湖南6个省;西部地区包括内蒙古、广西、重庆、四川、贵州、云南、西藏、陕西、甘肃、青海、宁夏、新疆12个省(市、自治区);东北地区包括辽宁、吉林、黑龙江3个省。   6、2021年一季度规模以上文化及相关产业企业营业收入的上年同期数与之前公布的2020年一季度数据存在差异,主要原因是:(一)规模以上企业数量发生增减变化。2021年一季度有部分企业达到规模以上标准纳入调查范围,也有部分企业因规模变小而退出调查范围,还有新建投产、破产注(吊)销企业等,使得规上企业数量发生变化。2021年一季度,全国规模以上文化及相关产业调查企业数量为6.3万家,比上年同期增加4191家。按照可比口径,2020年一季度全国规模以上文化及相关产业企业营业收入为18166亿元。(二)加强统计执法,对统计执法检查中发现的不符合规模以上要求的企业及填报的不实数据进行了清理,对相关基数依规进行了修正。 

国家统计局社科文司高级统计师张鹏解读一季度全国规模以上文化及相关产业企业营业收入数据

文化企业营业收入恢复性增长 文化新业态发展势头持续强劲 ——国家统计局社科文司高级统计师张鹏解读一季度全国规模以上文化及相关产业企业营业收入数据   随着统筹疫情防控和经济社会发展成果巩固拓展,一季度我国文化及相关产业持续稳定恢复,文化市场回暖迹象明显,文化新业态发展保持强劲势头。   一季度,全国规模以上文化及相关产业企业实现营业收入25498亿元,同比增长40.4%,比2019年一季度增长20.9%,两年平均增长10.0%(两年平均增速是指以2019年相应同期数为基数,采用几何平均的方法计算的增速),高于2019年一季度增速1.7个百分点,文化及相关产业发展基本恢复到疫情前水平。   文化各行业恢复势头良好。文化及相关产业9个行业营业收入全部实现正增长。其中,创意设计服务、文化消费终端生产、文化传播渠道同比增速高于全国平均水平,分别增长45.0%、46.2%和50.0%,两年平均分别增长18.9%、11.4%和1.3%;受去年疫情影响*严重的文化娱乐休闲服务,一季度实现强势反弹,同比增长101.8%,但两年平均仍下降9.2%;其他行业均呈现较快增长。   文化新业态引领作用明显。从文化及相关产业细分行业看,文化新业态特征较为明显的16个行业小类[1]实现营业收入8461亿元,同比增长39.8%,比2019年一季度增长61.5%,两年平均增长27.1%,高于全国平均水平17.1个百分点。其中,多媒体游戏动漫和数字出版软件开发、互联网广告服务、娱乐用智能无人飞行器制造、可穿戴智能文化设备制造等4个行业营业收入两年平均增速均超过30%。   文化企业集聚效应提升。从区域看,东部地区实现营业收入19690亿元,同比增长41.1%,两年平均增长10.2%;占全国的比重为77.2%,同比提高0.4个百分点。从城市群看,15个副省级城市[2]实现营业收入8479亿元,同比增长44.5%,两年平均增长12.1%;占全国的比重为33.3%,同比提高1.0个百分点。   下阶段,随着我国经济社会发展持续稳定恢复,文化供给规模持续扩大,文化消费需求进一步释放,文化及相关产业恢复有望稳中加固,稳中向好。   注:   [1] 新业态特征明显的16个行业小类是:广播电视集成播控,互联网搜索服务,互联网其他信息服务,数字出版,其他文化艺术业,动漫、游戏数字内容服务,互联网游戏服务,多媒体、游戏动漫和数字出版软件开发,增值电信文化服务,其他文化数字内容服务,互联网广告服务,互联网文化娱乐平台,版权和文化软件服务,娱乐用智能无人飞行器制造,可穿戴智能文化设备制造,其他智能文化消费设备制造。   [2] 15个副省级城市包括沈阳、大连、长春、哈尔滨、南京、杭州、宁波、厦门、济南、青岛、武汉、广州、深圳、成都和西安。 

2021年4月中国采购经理指数运行情况

国家统计局服务业调查中心中国物流与采购联合会   一、中国制造业采购经理指数运行情况   4月份,中国制造业采购经理指数(PMI)为51.1%,低于上月0.8个百分点,仍高于临界点,制造业继续保持扩张态势。    从企业规模看,大、中型企业PMI分别为51.7%和50.3%,低于上月1.0和1.3个百分点,小型企业PMI为50.8%,高于上月0.4个百分点。   从分类指数看,在构成制造业PMI的5个分类指数中,生产指数和新订单指数均高于临界点,原材料库存指数、从业人员指数和供应商配送时间指数均低于临界点。   生产指数为52.2%,比上月回落1.7个百分点,仍高于临界点,表明制造业生产扩张力度有所减弱。   新订单指数为52.0%,比上月回落1.6个百分点,仍高于临界点,表明制造业市场需求扩张有所放缓。   原材料库存指数为48.3%,比上月微降0.1个百分点,低于临界点,表明制造业主要原材料库存量较上月下降。   从业人员指数为49.6%,比上月下降0.5个百分点,低于临界点,表明制造业企业用工景气度有所回落。   供应商配送时间指数为48.7%,比上月下降1.3个百分点,低于临界点,表明制造业原材料供应商交货时间延长。 表1  中国制造业PMI及构成指数(经季节调整)                                              单位:%   PMI   生产 新订单 原材料 库存 从业人员 供应商配送时间 2020年4月 50.8 53.7 50.2 48.2 50.2 50.1 2020年5月 50.6 53.2 50.9 47.3 49.4 50.5 2020年6月 50.9 53.9 51.4 47.6 49.1 50.5 2020年7月 51.1 54.0 51.7 47.9 49.3 50.4 2020年8月 51.0 53.5 52.0 47.3 49.4 50.4 2020年9月 51.5 54.0 52.8 48.5 49.6 50.7 2020年10月 51.4 53.9 52.8 48.0 49.3 50.6 2020年11月 52.1 54.7 53.9 48.6 49.5 50.1 2020年12月 51.9 54.2 53.6 48.6 49.6 49.9 2021年1月 51.3 53.5 52.3 49.0 48.4 48.8 2021年2月 50.6 51.9 51.5 47.7 48.1 47.9 2021年3月 51.9 53.9 53.6 48.4 50.1 50.0 2021年4月 51.1 52.2 52.0 48.3 49.6 48.7  表2  中国制造业PMI其他相关指标情况(经季节调整)                                              单位:%   新出口 订单 进口 采购量 主要原材料购进价格 出厂 价格 产成品 库存 在手 订单 生产经营活动预期 2020年4月 33.5 43.9 52.0 42.5 42.2 49.3 43.6 54.0 2020年5月 35.3 45.3 50.8 51.6 48.7 47.3 44.1 57.9 2020年6月 42.6 47.0 51.8 56.8 52.4 46.8 44.8 57.5 2020年7月 48.4 49.1 52.4 58.1 52.2 47.6 45.6 57.8 2020年8月 49.1 49.0 51.7 58.3 53.2 47.1 46.0 58.6 2020年9月 50.8 50.4 53.6 58.5 52.5 48.4 46.1 58.7 2020年10月 51.0 50.8 53.1 58.8 53.2 44.9 47.2 59.3 2020年11月 51.5 50.9 53.7 62.6 56.5 45.7 46.7 60.1 2020年12月 51.3 50.4 53.2 68.0 58.9 46.2 47.1 59.8 2021年1月 50.2 49.8 52.0 67.1 57.2 49.0 47.3 57.9 2021年2月 48.8 49.6 51.6 66.7 58.5 48.0 46.1 59.2 2021年3月 51.2 51.1 53.1 69.4 59.8 46.7 46.6 58.5 2021年4月 50.4 50.6 51.7 66.9 57.3 46.8 46.4 58.3    二、中国非制造业采购经理指数运行情况   4月份,非制造业商务活动指数为54.9%,较上月回落1.4个百分点,非制造业扩张步伐有所放缓。    分行业看,建筑业商务活动指数为57.4%,低于上月4.9个百分点。服务业商务活动指数为54.4%,低于上月0.8个百分点。从行业情况看,铁路运输、航空运输、住宿、电信广播电视卫星传输服务等行业商务活动指数位于60.0%以上高位景气区间;保险业、其他金融业商务活动指数位于临界点以下。     新订单指数为51.5%,比上月回落4.4个百分点,仍高于临界点,表明非制造业市场需求扩张放缓。分行业看,建筑业新订单指数为52.4%,比上月回落6.6个百分点;服务业新订单指数为51.3%,比上月回落4.1个百分点。   投入品价格指数为54.9%,比上月回落1.3个百分点,表明非制造业企业用于经营活动的投入品价格涨幅收窄。分行业看,建筑业投入品价格指数为64.7%,比上月上升1.1个百分点;服务业投入品价格指数为53.1%,比上月回落1.7个百分点。   销售价格指数为51.2%,比上月回落1.0个百分点,仍高于临界点,表明非制造业销售价格总体涨幅收窄。分行业看,建筑业销售价格指数为56.7%,比上月上升1.7个百分点;服务业销售价格指数为50.2%,比上月回落1.5个百分点。   从业人员指数为48.7%,比上月下降1.0个百分点,表明非制造业用工量较上月有所减少。分行业看,建筑业从业人员指数为51.0%,比上月回落4.0个百分点;服务业从业人员指数为48.3%,比上月下降0.5个百分点。   业务活动预期指数为63.0%,比上月回落0.7个百分点,仍位于高位景气区间,表明非制造业企业对行业发展信心稳定。分行业看,建筑业业务活动预期指数为64.8%,比上月回落3.3个百分点;服务业业务活动预期指数为62.7%,比上月回落0.2个百分点。 表3  中国非制造业主要分类指数(经季节调整)                                             单位:%    商务活动 新订单 投入品 价格 销售价格 从业人员 业务活动 预期 2020年4月 53.2 52.1 49.0 45.4 48.6 60.1 2020年5月 53.6 52.6 52.0 48.6 48.5 63.9 2020年6月 54.4 52.7 52.9 49.5 48.7 60.3 2020年7月 54.2 51.5 53.0 50.1 48.1 62.2 2020年8月 55.2 52.3 51.9 50.1 48.3 62.1 2020年9月 55.9 54.0 50.6 48.9 49.1 63.0 2020年10月 56.2 53.0 50.9 49.4 49.4 62.9 2020年11月 56.4 52.8 52.7 51.0 48.9 61.2 2020年12月 55.7 51.9 54.3 52.3 48.7 60.6 2021年1月 52.4 48.7 54.5 51.4 47.8 55.1 2021年2月 51.4 48.9 54.7 50.1 48.4 64.0 2021年3月 56.3 55.9 56.2 52.2 49.7 63.7 2021年4月 54.9 51.5 54.9 51.2 48.7 63.0  表4  中国非制造业其他分类指数(经季节调整)                                              单位:%   新出口订单 在手订单 存货 供应商配送时间 2020年4月 35.5 43.4 47.0 51.0 2020年5月 41.3 44.3 47.8 52.9 2020年6月 43.3 44.8 48.0 52.1 2020年7月 44.5 44.9 48.1 51.9 2020年8月 45.1 44.6 48.5 52.4 2020年9月 49.1 46.3 48.5 52.2 2020年10月 47.0 44.9 48.7 52.3 2020年11月 49.0 45.2 48.8 51.8 2020年12月 47.5 44.7 47.0 51.2 2021年1月 48.0 44.0 47.4 49.8 2021年2月 45.7 44.0 45.9 49.8 2021年3月 50.3 45.9 48.2 51.8 2021年4月 48.1 45.8 47.2 50.9    三、中国综合PMI产出指数运行情况   4月份,综合PMI产出指数为53.8%,比上月回落1.5个百分点,表明我国企业生产经营活动总体扩张力度减弱。    附注   1.主要指标解释   采购经理指数(PMI),是通过对企业采购经理的月度调查结果统计汇总、编制而成的指数,它涵盖了企业采购、生产、流通等各个环节,包括制造业和非制造业领域,是国际上通用的监测宏观经济走势的先行性指数之一,具有较强的预测、预警作用。综合PMI产出指数是PMI指标体系中反映当期全行业(制造业和非制造业)产出变化情况的综合指数。PMI高于50%时,反映经济总体较上月扩张;低于50%,则反映经济总体较上月收缩。   2.调查范围   涉及《国民经济行业分类》(GB/T4754-2017)中制造业的31个行业大类,3000家调查样本;非制造业的43个行业大类,4200家调查样本。   3.调查方法   采购经理调查采用PPS(Probability Proportional to Size)抽样方法,以制造业或非制造业行业大类为层,行业样本量按其增加值占全部制造业或非制造业增加值的比重分配,层内样本使用与企业主营业务收入成比例的概率抽取。   本调查由国家统计局直属调查队具体组织实施,利用国家统计联网直报系统对企业采购经理进行月度问卷调查。   4.计算方法   (1)分类指数的计算方法。制造业采购经理调查指标体系包括生产、新订单、新出口订单、在手订单、产成品库存、采购量、进口、主要原材料购进价格、出厂价格、原材料库存、从业人员、供应商配送时间、生产经营活动预期等13个分类指数。非制造业采购经理调查指标体系包括商务活动、新订单、新出口订单、在手订单、存货、投入品价格、销售价格、从业人员、供应商配送时间、业务活动预期等10个分类指数。分类指数采用扩散指数计算方法,即正向回答的企业个数百分比加上回答不变的百分比的一半。由于非制造业没有合成指数,国际上通常用商务活动指数反映非制造业经济发展的总体变化情况。   (2)制造业PMI指数的计算方法。制造业PMI是由5个扩散指数(分类指数)加权计算而成。5个分类指数及其权数是依据其对经济的先行影响程度确定的。具体包括:新订单指数,权数为30%;生产指数,权数为25%;从业人员指数,权数为20%;供应商配送时间指数,权数为15%;原材料库存指数,权数为10%。其中,供应商配送时间指数为逆指数,在合成制造业PMI指数时进行反向运算。   (3)综合PMI产出指数的计算方法。综合PMI产出指数由制造业生产指数与非制造业商务活动指数加权求和而成,权数分别为制造业和非制造业占GDP的比重。   5.季节调整   采购经理调查是一项月度调查,受季节因素影响,数据波动较大。现发布的指数均为季节调整后的数据。