- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章深入理解Python中各种方法的运作原理由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
方法在Python中是如何工作的 。
方法就是一个函数,它作为一个类属性而存在,你可以用如下方式来声明、访问一个函数: 。
1
2
3
4
5
6
7
8
|
>>>
class
Pizza(
object
):
...
def
__init__(
self
, size):
...
self
.size
=
size
...
def
get_size(
self
):
...
return
self
.size
...
>>> Pizza.get_size
<unbound method Pizza.get_size>
|
Python在告诉你,属性_get_size是类Pizza的一个未绑定方法。这是什么意思呢?很快我们就会知道答案: 。
1
2
3
4
|
>>> Pizza.get_size()
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
TypeError: unbound method get_size() must be called with Pizza instance as first argument (got nothing instead)
|
我们不能这么调用,因为它还没有绑定到Pizza类的任何实例上,它需要一个实例作为第一个参数传递进去(Python2必须是该类的实例,Python3中可以是任何东西),尝试一下: 。
1
|
>>> Pizza.get_size(Pizza(
42
))
|
太棒了,现在用一个实例作为它的的第一个参数来调用,整个世界都清静了,如果我说这种调用方式还不是最方便的,你也会这么认为的;没错,现在每次调用这个方法的时候我们都不得不引用这个类,如果不知道哪个类是我们的对象,长期看来这种方式是行不通的.
那么Python为我们做了什么呢,它绑定了所有来自类_Pizza的方法以及该类的任何一个实例的方法。也就意味着现在属性get_size是Pizza的一个实例对象的绑定方法,这个方法的第一个参数就是该实例本身。 。
1
2
3
4
|
>>> Pizza(
42
).get_size
<bound method Pizza.get_size of <__main__.Pizza
object
at
0x7f3138827910
>>
>>> Pizza(
42
).get_size()
42
|
和我们预期的一样,现在不再需要提供任何参数给_get_size,因为它已经是绑定的,它的self参数会自动地设置给Pizza实例,下面代码是最好的证明: 。
1
2
3
|
>>> m
=
Pizza(
42
).get_size
>>> m()
42
|
更有甚者,你都没必要使用持有Pizza对象的引用了,因为该方法已经绑定到了这个对象,所以这个方法对它自己来说是已经足够了.
也许,如果你想知道这个绑定的方法是绑定在哪个对象上,下面这种手段就能得知: 。
1
2
3
4
5
6
7
8
|
>>> m
=
Pizza(
42
).get_size
>>> m.__self__
<__main__.Pizza
object
at
0x7f3138827910
>
>>>
# You could guess, look at this:
...
>>> m
=
=
m.__self__.get_size
True
|
显然,该对象仍然有一个引用存在,只要你愿意你还是可以把它找回来.
在Python3中,依附在类上的函数不再当作是未绑定的方法,而是把它当作一个简单地函数,如果有必要它会绑定到一个对象身上去,原则依然和Python2保持一致,但是模块更简洁: 。
1
2
3
4
5
6
7
8
|
>>>
class
Pizza(
object
):
...
def
__init__(
self
, size):
...
self
.size
=
size
...
def
get_size(
self
):
...
return
self
.size
...
>>> Pizza.get_size
<function Pizza.get_size at
0x7f307f984dd0
>
|
静态方法 。
静态方法是一类特殊的方法,有时你可能需要写一个属于这个类的方法,但是这些代码完全不会使用到实例对象本身,例如: 。
1
2
3
4
5
6
7
|
class
Pizza(
object
):
@staticmethod
def
mix_ingredients(x, y):
return
x
+
y
def
cook(
self
):
return
self
.mix_ingredients(
self
.cheese,
self
.vegetables)
|
这个例子中,如果把_mix_ingredients作为非静态方法同样可以运行,但是它要提供self参数,而这个参数在方法中根本不会被使用到。这里的@staticmethod装饰器可以给我们带来一些好处:
Python不再需要为Pizza对象实例初始化一个绑定方法,绑定方法同样是对象,但是创建他们需要成本,而静态方法就可以避免这些。 。
1
2
3
4
5
6
|
>>> Pizza().cook
is
Pizza().cook
False
>>> Pizza().mix_ingredients
is
Pizza.mix_ingredients
True
>>> Pizza().mix_ingredients
is
Pizza().mix_ingredients
True
|
可读性更好的代码,看到@staticmethod我们就知道这个方法并不需要依赖对象本身的状态。 可以在子类中被覆盖,如果是把mix_ingredients作为模块的顶层函数,那么继承自Pizza的子类就没法改变pizza的mix_ingredients了如果不覆盖cook的话.
类方法 。
话虽如此,什么是类方法呢?类方法不是绑定到对象上,而是绑定在类上的方法。 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
>>>
class
Pizza(
object
):
... radius
=
42
... @
classmethod
...
def
get_radius(
cls
):
...
return
cls
.radius
...
>>>
>>> Pizza.get_radius
<bound method
type
.get_radius of <
class
'__main__.Pizza'
>>
>>> Pizza().get_radius
<bound method
type
.get_radius of <
class
'__main__.Pizza'
>>
>>> Pizza.get_radius
is
Pizza().get_radius
True
>>> Pizza.get_radius()
42
|
无论你用哪种方式访问这个方法,它总是绑定到了这个类身上,它的第一个参数是这个类本身(记住:类也是对象).
什么时候使用这种方法呢?类方法通常在以下两种场景是非常有用的:
工厂方法:它用于创建类的实例,例如一些预处理。如果使用@staticmethod代替,那我们不得不硬编码Pizza类名在函数中,这使得任何继承Pizza的类都不能使用我们这个工厂方法给它自己用。 。
1
2
3
4
5
6
7
|
class
Pizza(
object
):
def
__init__(
self
, ingredients):
self
.ingredients
=
ingredients
@classmethod
def
from_fridge(
cls
, fridge):
return
cls
(fridge.get_cheese()
+
fridge.get_vegetables())
|
调用静态类:如果你把一个静态方法拆分成多个静态方法,除非你使用类方法,否则你还是得硬编码类名。使用这种方式声明方法,Pizza类名明永远都不会在被直接引用,继承和方法覆盖都可以完美的工作.
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class
Pizza(
object
):
def
__init__(
self
, radius, height):
self
.radius
=
radius
self
.height
=
height
@staticmethod
def
compute_area(radius):
return
math.pi
*
(radius
*
*
2
)
@classmethod
def
compute_volume(
cls
, height, radius):
return
height
*
cls
.compute_area(radius)
def
get_volume(
self
):
return
self
.compute_volume(
self
.height,
self
.radius)
|
抽象方法 。
抽象方法是定义在基类中的一种方法,它没有提供任何实现,类似于Java中接口(Interface)里面的方法.
在Python中实现抽象方法最简单地方式是: 。
1
2
3
|
class
Pizza(
object
):
def
get_radius(
self
):
raise
NotImplementedError
|
任何继承自_Pizza的类必须覆盖实现方法get_radius,否则会抛出异常.
这种抽象方法的实现有它的弊端,如果你写一个类继承Pizza,但是忘记实现get_radius,异常只有在你真正使用的时候才会抛出来。 。
1
2
3
4
5
6
7
|
>>> Pizza()
<__main__.Pizza
object
at
0x7fb747353d90
>
>>> Pizza().get_radius()
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
File
"<stdin>"
, line
3
,
in
get_radius
NotImplementedError
|
还有一种方式可以让错误更早的触发,使用Python提供的abc模块,对象被初始化之后就可以抛出异常: 。
1
2
3
4
5
6
7
8
9
|
import
abc
class
BasePizza(
object
):
__metaclass__
=
abc.ABCMeta
@abc
.abstractmethod
def
get_radius(
self
):
"""Method that should do something."""
|
使用abc后,当你尝试初始化BasePizza或者任何子类的时候立马就会得到一个TypeError,而无需等到真正调用get_radius的时候才发现异常。 。
1
2
3
4
|
>>> BasePizza()
Traceback (most recent call last):
File
"<stdin>"
, line
1
,
in
<module>
TypeError: Can't instantiate abstract
class
BasePizza with abstract methods get_radius
|
混合静态方法、类方法、抽象方法 。
当你开始构建类和继承结构时,混合使用这些装饰器的时候到了,所以这里列出了一些技巧.
记住,声明一个抽象的方法,不会固定方法的原型,这就意味着虽然你必须实现它,但是我可以用任何参数列表来实现: 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import
abc
class
BasePizza(
object
):
__metaclass__
=
abc.ABCMeta
@abc
.abstractmethod
def
get_ingredients(
self
):
"""Returns the ingredient list."""
class
Calzone(BasePizza):
def
get_ingredients(
self
, with_egg
=
False
):
egg
=
Egg()
if
with_egg
else
None
return
self
.ingredients
+
egg
|
这样是允许的,因为Calzone满足BasePizza对象所定义的接口需求。同样我们也可以用一个类方法或静态方法来实现: 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import
abc
class
BasePizza(
object
):
__metaclass__
=
abc.ABCMeta
@abc
.abstractmethod
def
get_ingredients(
self
):
"""Returns the ingredient list."""
class
DietPizza(BasePizza):
@staticmethod
def
get_ingredients():
return
None
|
这同样是正确的,因为它遵循抽象类BasePizza设定的契约。事实上get_ingredients方法并不需要知道返回结果是什么,结果是实现细节,不是契约条件.
因此,你不能强制抽象方法的实现是一个常规方法、或者是类方法还是静态方法,也没什么可争论的。从Python3开始(在Python2中不能如你期待的运行,见issue5867),在abstractmethod方法上面使用@staticmethod和@classmethod装饰器成为可能。 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import
abc
class
BasePizza(
object
):
__metaclass__
=
abc.ABCMeta
ingredient
=
[
'cheese'
]
@classmethod
@abc
.abstractmethod
def
get_ingredients(
cls
):
"""Returns the ingredient list."""
return
cls
.ingredients
|
别误会了,如果你认为它会强制子类作为一个类方法来实现get_ingredients那你就错了,它仅仅表示你实现的get_ingredients在BasePizza中是一个类方法.
可以在抽象方法中做代码的实现?没错,Python与Java接口中的方法相反,你可以在抽象方法编写实现代码通过super()来调用它。(译注:在Java8中,接口也提供的默认方法,允许在接口中写方法的实现) 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import
abc
class
BasePizza(
object
):
__metaclass__
=
abc.ABCMeta
default_ingredients
=
[
'cheese'
]
@classmethod
@abc
.abstractmethod
def
get_ingredients(
cls
):
"""Returns the ingredient list."""
return
cls
.default_ingredients
class
DietPizza(BasePizza):
def
get_ingredients(
self
):
return
[
'egg'
]
+
super
(DietPizza,
self
).get_ingredients()
|
这个例子中,你构建的每个pizza都通过继承BasePizza的方式,你不得不覆盖get_ingredients方法,但是能够使用默认机制通过super()来获取ingredient列表.
最后此篇关于深入理解Python中各种方法的运作原理的文章就讲到这里了,如果你想了解更多关于深入理解Python中各种方法的运作原理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
最近做一个项目,由于是在别人框架里开发app,导致了很多限制,其中一个就是不能直接引用webservice 。 我们都知道,调用webserivice 最简单的方法就是在 "引用"
这是SDL2代码的一部分 SDL主函数 int main(int argc,char *argv[]) { ... ... bool quit=false; S
c 中的函数: PHPAPI char *php_pcre_replace(char *regex, int regex_len, ch
我有以下映射: public class SecurityMap : ClassMap { public SecurityMap() {
我在vue-lic3中使用了SCSS,但是有一个奇怪的错误,使用/ deep /会报告错误,我不想看到它。 代码运行环境 vue-cli3 + vant + scss 的CSS /deep/ .van
我在深入阅读 C# 时遇到了这个我能理解的内容: 当它被限制为引用类型时,执行的比较类型完全取决于类型参数被限制为什么。 但是不能理解这个: 如果进一步限制派生自重载 == 和 != 运算符的特定类型
Closed. This question is opinion-based。它当前不接受答案。 想改善这个问题吗?更新问题,以便editing this post用事实和引用来回答。 3年前关闭。
有人可以详细介绍关于自赋值的运算符重载中的 *this 和 const 例如: Class& Class::operator=(const Class& other) { a = other.
在向树中插入新节点时,如何填充闭包表的深度/长度列? ancestor 和 descendant 中的值是来自另一个表的 ID,表示要以树结构排列的页面。 关闭表: ancestor desce
现在我正在阅读“深入了解 C#”。缺少的一件事是完成一章后我可以解决的一系列问题。那会帮助我理解我刚刚学到的概念。 哪里可以找到适合 C#3.0 的问题集? 谢谢 最佳答案 你可以试试LINQ 101
TypeScript 给 JavaScript 扩展了类型的语法,我们可以给变量加上类型,在编译期间会做类型检查,配合编辑器还能做更准确的智能提示。此外,TypeScript 还支持了高级类型用
是否有一个单行代码来获取生成器并生成该生成器中的所有元素?例如: def Yearly(year): yield YEARLY_HEADER for month in range(1, 13)
所以我阅读了一些与“什么是方法组”相关的 StackOverflow 问题以及其他互联网文章,它们在底线都说了同样的话——方法组是“一组重载方法” ". 但是,在阅读 Jon Skeet 的“C# 深
有什么方法可以从子组件中获取子组件吗? 想象一下以下组件树: 应用程序 问题 问题选项(包含复选框) 问题选项(包含复选框) 问题选项(包含复选框) 我想从 App 访问问题选项以选中所有复选框。 参
class_eval 和 instance_eval 在定义方法等情况下是完全可以预测的。我也理解类的实例和类的单例(又名特征类)之间的区别。 但是 我无法弄清楚以下唯一的事情:比方说,出于某些策略目
我想出了如何将符号 rwx 部分读取/转换为 421 个八进制部分,这非常简单。但是当涉及到特殊字符时,我感到非常困惑。我们知道 -r-xr---wx 转换为 0543,但 -r-sr---wt 或
我怀疑我系统的 Java 版本有问题。某些应用程序出现段错误或内存不足或存在链接错误。如果我从源代码安装了 JDK,我会做类似“make test”的事情,看看哪些测试失败了。但是,看起来从源代码构建
如何克隆一个 repo(使用 libgit2 ) 我想做什么git clone确实,但有 libgit2 .我可能要问的是什么 git clone确实很深入。 这是我目前正在做的: 初始化一个repo
00、头痛的JS闭包、词法作用域? 被JavaScript的闭包、上下文、嵌套函数、this搞得很头痛,这语言设计的,感觉比较混乱,先勉强理解总结一下😂😂😂.
我开始玩 lubridate R 中的包。我注意到 now(tzone="EST")计算为: [1] "2015-08-25 13:01:08 EST" 而 now(tzone="PST")导致警告:
我是一名优秀的程序员,十分优秀!