Python3 - 开始python编程(六)
在上一篇文章中,我们介绍了函数和作用域。本文重点讲解代码可重用性。
它使我们对如何将可重复的代码分解为函数以及变量的范围如何影响程序的输出有了很好的了解。
今天,我们将学习一种也可以被视为类型的新型对象。可以将这种新类型的对象视为容器或蓝图,它为类似的代码段定义了称为namespace的特殊作用域。
这个新对象称为 class,它确实为清理和组织代码创造了奇迹。在学习本教程的过程中,我希望您想到一栋房子的蓝图,或者想像一下上图中所有这些星星的曲奇刀。这不是房子,也不是cookie,但它可以确保蓝图的每个_instance_都完全相同,这正是类的作用。
类
简而言之,类是包含变量和函数的对象,但实际上,它们不仅仅充当容器。类提供了一种创建可重复使用的代码的方法,该代码可以反复使用。
我将回过头来参考指针(您知道这是有原因的)。当您创建一个类时,您将创建一个引用。也就是说,指向内存中将存储该类的块,并对其进行引用以创建该类的新实例或副本,然后将其复制到具有新值的新内存块中。这听起来可能令人困惑,所以也许一些示例会有所帮助:
1
2
3
4
5
6
7
8
9
10
class House():
def __init__(self, number_of_windows, number_of_doors):
self.number_of_windows = number_of_windows
self.number_of_doors = number_of_doors
my_house = House(number_of_windows=2, number_of_doors=1)
your_house = House(4, 2)
print(my_house.number_of_doors) # prints 1
print(your_house.number_of_windows) # prints 4
我们使用语法class <name>()
定义一个类。类使用Pascal驼峰命名,这意味着没有空格,并且每个单词的首字母大写。在此示例中,我将创建一个新的”House”类。括号是必需的,我将解释原因。与往常一样,我们需要在末尾使用冒号来包含类的主体。
您会在这里看到一些有趣的东西:def __init __(self)
。这个就是类的初始化函数。经验丰富的程序员喜欢将此称为构造(constructor)方法。
方法和函数在句子中使用时,大部分是可互换的。如果您使用错误,大多数程序员不会纠正您,但是函数和方法之间的区别是方法属于类对象,而函数不属于类对象。
init两端的双下划线或__,用于重载或替换默认方法。默认情况下,类包含一个__init __(self)方法,因此,如果您不需要做任何事情,则无需添加此方法。
但是我在构造函数方法中不仅使用了”self”,还有其他一些参数:”number_of_windows”和”number_of_doors”。我认为很容易理解它们的作用,因为我们在命名变量方面越来越好,但是为什么我们需要添加它们呢?
在构造函数方法中,添加参数以使我们能够为类提供初始值总是很有用的。在这种情况下,我们增加门窗的数量。但这并不能解决问题。如果您只是将参数传递给常规函数,而对它们不执行任何操作,则什么都不会发生,对吧!我们需要将它们分配给班级,但是范围存在问题。
我在上一篇文章中告诉您,Python为您处理范围。这就是其中一个例子。我们需要将传递给构造方法的值分配给属于该类实例的变量。为此,我们使用关键字”self”。 self是这个对象,不是类,而是要创建的对象课堂上。
self.number_of_windows是属性,用于存储传递给number_of_windows参数的数字。点.
在这里很重要。如果self
引用该类的实例,并且self.number_of_windows
是该实例中要存储此值的属性,则这必须表示.
必须表示该属性是的子对象。.
是我们访问类的属性和方法的方式。
属性与方法非常相似,区别在于它们指的是类中的变量。是的,它们有时可以互换使用,您很可能不会得到纠正。
创建新课程时,您会注意到我有以下几行:
1
2
my_house = House(number_of_windows=2, number_of_doors=1)
your_house = House(4, 2)
这两种都是正确的语法,只是其中一种使用了“命名参数”。如果您错过了我在函数上的文章,我将对此进行一些介绍,但此处同样适用。如果您使用命名参数,那么每个人都可以轻松理解值的含义,而无需深入研究代码。你可以以任意顺序输入这些命名参数。另外,请注意这里没有self
,调用方法时不使用self
。
如果选择像对your_house那样省略命名的参数,则必须对参数使用与构造方法中出现的顺序相同的顺序。这意味着窗户数为4,门数为2。
最后,我们打印每个房屋的门窗数量。 “my_house”只有一扇门,因此会打印数字”1”。你的房子比我的要好,所以可以打印4个窗口。我可以打印your_house.number_of_doors
,它会显示2
。原因是”my_house”和”your_house”是”House”的两个不同实例。即使它们是从同一类派生的,它们还是两个单独的实体。
那为什么“自我”如此重要?我们还没有介绍_static方法_,但是我们可以快速浏览它们。静态方法是不依赖于类属性或方法起作用的方法。让我们看一个例子。
1
2
3
4
5
6
7
8
9
10
11
class Dog():
def __init__(self, name, age):
self.name = name
self.age = age
def get_description(self):
return self.name + " is " + self.age + " years old."
@staticmethod
def speak():
print("Woof!")
在这里,我们有一个“Dog”类。跳过构造函数,我们有了第一个方法。这是一种类方法,因此我们包含”self”。我们这样做是因为它引用了此实例的name
和age
属性。但是,所有的狗天生都有说话的能力。当我们提到狗时,我们知道它们应该Woof!。因为我们知道这是事实,我们可以通过在名为”staticmethod”的方法之上添加装饰器(decorator)使其成为静态方法。 “@”表示应该装饰下面的函数,而且是必需的。
通过使用@staticmethod装饰器,我们不必使用self
。那么“自我”有什么重要呢?如果使用self
,则必须有一个类的实例。如果没有类的实例,则不能使用该属性或属于该类的方法。通过使用静态方法,我们可以调用此方法而无需创建类的实例。
这意味着我们可以使用Dog.speak()
,然后将Woof!
打印到终端。 “Datetime”是使用静态方法的一个很好的例子。
1
from datetime import datetimedatetime.now()
现在不必担心第一行,我们将在以后介绍。在这里,您可以看到datetime
类具有一个名为now()
的静态方法,该方法从系统中返回当前日期时间。我能够调用now
方法,而无需创建datetime
类。
如果您的方法未标记为静态,则需要先创建该类的新实例,然后才能使用它。
关于“狗”类中的”get_description(self)”方法的一些注意事项:如果从类中调用此方法,则必须使用”self.get_description()”。仅仅因为它是此类的一种方法,并不意味着它就不受”self”的约束。从类中的另一个方法调用静态方法speak()
时必须使用self
进行调用。
属性
将属性添加到类时,作用域很重要。比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Person1(object):
"""Creates a new Person"""
name = "Unknown"
fred = Person1()
wilma = Person1()
fred.name = "fred"
wilma.name = "wilma"
print(fred.name) # prints "wilma"
print(wilma.name) # prints "wilma"
#-----------------------------------------
class Person2(object):
"""Creates a new Person"""
def __init__(self, name):
self.name = name
fred = Person2("fred")
wilma = Person2("wilma")
print(fred.name) # prints "fred"
print(wilma.name) # prints "wilma"
有什么不同?在”Person1”中,我们添加了一个名为name的属性,然后从”Person1”中创建了”fred”和”wilma”。我们有两个实例,我们将每个实例设置为其各自的名称。
当我们显示”fred.name”时,它会错误地打印”wilma”,但是当我们显示”wilma.name”时,它将正确地打印”wilma”。为什么?
当你创建一个顶级属性而不使用”self。”,就像我们在构造方法中所做的那样,它成为一个类属性。
类属性和实例属性之间的区别在于,类属性在该类的所有实例之间共享,而实例属性仅可用于该实例。
Scope使我们到了这里,但是如果我们将name
更改为self.name
,我们的代码将是正确的;但是,在实例创建时,我们没有构造函数方法可为self.name
提供初始值。
在第二个示例中,我们不使用self.name
作为类中的属性,但是,我们将构造函数中的参数name
分配给self.name
。在幕后,当您使用self.something时,Python会自动将作用域扩大到类的顶层,并将对象标记为实例属性而不是类属性。
1
2
3
4
5
6
7
8
class Dog(object):
self.name
def __init__(self, name):
self.name = name
# is the same as
class Dog():
def __init__(self, name):
self.name = name
因此,请小心如何创建类,并始终尝试使用构造函数。
摘要
今天,我们讨论了类,方法和属性。我还介绍了一些有关初始化的信息。在下一个主题中,我将详细介绍初始化,但是我确实想涵盖更多与类相关的项目,包括更多的覆盖,子类,反初始化和引用计数。
建议阅读
这是写使课堂变得更加有趣的东西,希望您正在练习所学的内容。