Skip to main content
 首页 » 编程设计

python之从 Javascript 到 Python之了解类、方法和属性的工作原理

2025年05月04日25wuhuacong

在 Javascript 中,有多种方法可以继承方法。 下面是使用几种方法的混合示例:

A = { 
    name: 'first', 
    wiggle: function() { return this.name + " is wiggling" }, 
    shake: function() { return this.name + " is shaking" } 
} 
 
B = Object.create(A) 
B.name = 'second' 
B.bop = function() { return this.name + ' is bopping' } 
 
 
C = function(name) { 
    obj = Object.create(B) 
    obj.name = name 
    obj.crunk = function() { return this.name + ' is crunking'} 
 
    return obj 
} 
 
final = new C('third') 

这为我提供了以下继承层次结构。

需要注意的重要事项之一是 name每个对象的属性。当运行一个方法时,即使在原型(prototype)链的最下游,由 this 定义的本地上下文关键字确保使用 localmost 属性/变量。

我最近转向使用 Python,但我无法理解子类如何访问父类(super class)方法,以及变量作用域/对象属性如何工作。

我在 Scrapy 中创建了一个 Spider,它(非常成功地)在一个域上抓取了 2000 多个页面并将它们解析为我需要的格式。许多助手只在主程序中运行 parse_response方法,我可以直接在数据上使用。原来的蜘蛛看起来像这样:

from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor 
from spider_scrape.items import SpiderItems 
 
class ScrapeSpider(CrawlSpider): 
 
    name              =   "myspider" 
    allowed_domains   =   ["domain.com.au"] 
    start_urls        =   ['https://www.domain.com.au/'] 
    rules             =   (Rule(SgmlLinkExtractor(allow=()),  
                                                  callback="parse_items",  
                                                  follow=True), ) 
 
    def parse_items(self, response): 
        ... 

回调函数 parse_items 包含为我处理响应的逻辑。当我概括所有内容时,我得到了以下结果(目的是在多个域中使用它):

#Base 
 
class BaseSpider(CrawlSpider): 
    """Base set of configuration rules and helper methods""" 
 
    rules = (Rule(LinkExtractor(allow=()), 
                                    callback="parse_response", 
                                    follow=True),) 
 
    def parse_response(self, response): 
            ... 
 
        def clean_urls(string): 
          """remove absolute URL's from hrefs, if URL is form an external domain do nothing""" 
          for domain in allowed_domains: 
              string = string.replace('http://' + domain, '') 
              string = string.replace('https://' + domain, '') 
          if 'http' not in string: 
              string = "/custom/files" + string 
          return string 
 
 
#Specific for each domain I want to crawl 
class DomainSpider(BaseSpider): 
 
    name = 'Domain' 
    allowed_domains = ['Domain.org.au'] 
    start_urls      = ['http://www.Domain.org.au/' 
                      ,'http://www.Domain.org.au/1'] 

当我通过 Scrapy 命令行运行它时,控制台出现以下错误:

经过一些测试后,将列表推导式更改为此使其工作:for domain in self.allowed_domains:

很好,这看起来与 this 非常相似Javascript 中的关键字 - 我使用对象的属性来获取值。还有更多的变量/属性将保存抓取所需的 XPath 表达式:

class DomainSpider(BaseSpider): 
 
    name = 'Domain' 
    page_title      =      '//title' 
    page_content    =      '//div[@class="main-content"]' 

更改 Spider 的其他部分以模仿 allowed_domains 变量的部分,我收到此错误:

我尝试以几种不同的方式设置属性,包括使用 self.page_content和/或 __init__(self)构造函数没有成功,但有不同的错误。

我完全不知道这里发生了什么。我期望发生的行为是:

  1. 当我运行 scrapy crawl <spider name>从终端实例化 DomainSpider 类
  2. 该类中的任何类常量都可用于它所继承的所有方法,类似于 Javascript 及其 this关键词
  3. 由于上下文,忽略来自其父类(super class)的任何类常量。

如果有人能

  • 向我解释以上内容
  • 向我指出比 LPTHW 更丰富但不是使用 Python 进行 TDD 的内容,这将是惊人的。

提前致谢。

请您参考如下方法:

我不熟悉 JavaScript,但与您的问题类似的问题总是包含一个答案,表明您必须学习使用 Python 的方法,而不是试图强制 Python 像您的其他语言。尝试在 Python 中重新创建您的 Javascript 风格,我想到了这个:

class A(object): 
    def __init__(self): 
        self.name = 'first' 
    def wiggle(self): 
        return self.name + ' is wiggling' 
    def shake(self): 
        return self.name + ' is shaking' 

创建 A 的实例,更改其名称并添加方法属性到实例

b = A() 
b.name = 'second' 
b.bop = lambda : b.name + ' is bopping' 

返回 A 实例的函数,带有附加属性 crunk。我不认为你的例子是这样,thing 不会有 bop 方法,尽管函数中的另一个语句可以添加一个。

def c(name): 
    thing = A() 
    thing.name = name 
    thing.crunk = lambda : thing.name + ' is crunking' 
    return thing 
 
final = c('third') 

没有任何继承正在进行,只有 A 的实例具有额外的属性。您得到以下结果:

>>>  
>>> b.name 
'second' 
>>> b.bop() 
'second is bopping' 
>>> b.shake() 
'second is shaking' 
>>> b.wiggle() 
'second is wiggling' 
>>>  
>>> final.name 
'third' 
>>> final.crunk() 
'third is crunking' 
>>> final.shake() 
'third is shaking' 
>>> final.wiggle() 
'third is wiggling' 
>>> final.bop() 
 
Traceback (most recent call last): 
  File "<pyshell#32>", line 1, in <module> 
    final.bop() 
AttributeError: 'A' object has no attribute 'bop' 
>>>  

在 Python 中你会这样做:

A 类具有 name 属性的默认参数和两个将绑定(bind)到 A 实例的方法。 name 是一个instance 属性,因为它是在__init__ 中定义的。 A 的 instances 将具有 name 属性 - A.name 将引发 AttributeError。

class A(object): 
    def __init__(self, name = 'first'): 
        self.name = name 
    def wiggle(self): 
        return self.name + ' is wiggling' 
    def shake(self): 
        return self.name + ' is shaking' 

Foo 继承了 A 的所有内容并定义了一个附加属性 bop。

class Foo(A): 
    def bop(self): 
        return self.name + ' is bopping' 

Bar 继承了 Foo 的所有内容并定义了一个附加属性 crunk

class Bar(Foo): 
    def crunk(self): 
        return self.name + ' is crunking' 

Baz 继承了 Bar 的所有内容并覆盖了 wiggle

class Baz(Bar): 
    def wiggle(self): 
        return 'This Baz instance, ' + self.name + ', is wiggling' 
 
foo = Foo('second') 
bar = Bar('third') 
baz = Baz('fourth') 

用法:

>>>  
>>> foo.name 
'second' 
>>> foo.bop() 
'second is bopping' 
>>> foo.shake() 
'second is shaking' 
>>> foo.wiggle() 
'second is wiggling' 
>>>  
>>> bar.name 
'third' 
>>> bar.bop() 
'third is bopping' 
>>> bar.shake() 
'third is shaking' 
>>> bar.wiggle() 
'third is wiggling' 
>>> bar.crunk() 
'third is crunking' 
>>>  
>>> baz.wiggle() 
'This Baz instance, fourth, is wiggling' 
>>> 

这些示例中的类具有仅对类实例有效的方法属性 - 方法需要绑定(bind)到实例。我没有包含任何不需要绑定(bind)到实例的类方法或静态方法的示例 - What is the difference between @staticmethod and @classmethod in Python? 有一些很好的答案

>>> A.wiggle 
<unbound method A.wiggle> 
>>> A.wiggle() 
 
Traceback (most recent call last): 
  File "<pyshell#41>", line 1, in <module> 
    A.wiggle() 
TypeError: unbound method wiggle() must be called with A instance as first argument (got nothing instead) 
>>> Bar.crunk 
<unbound method Bar.crunk> 
>>> Bar.crunk() 
 
Traceback (most recent call last): 
  File "<pyshell#43>", line 1, in <module> 
    Bar.crunk() 
TypeError: unbound method crunk() must be called with Bar instance as first argument (got nothing instead) 
>>>