首页 > 解决方案 > Python - AttributeError:'function'对象没有属性'rank'

问题描述

我正在通过自己编写代码和观看Youtube 视频来解决不同的 Project Euler 问题。

在处理问题 54 时,Youtube 视频的作者在 Python 2.7 中定义函数时编写的代码是

def __init__(self, *args):
    self.cards = sorted(args, key=lambda c: -c.rank)
    self.duplicates = {}
    for card in self.cards:
        if card.rank in self.duplicates:
            continue
        _duplicates = 0
        for card2 in self.cards:
            if card.rank == card2.rank:
                _duplicates += 1
        self.duplicates[card.rank] = _duplicates

这对他来说似乎很好。当我在 Python 3.0 上运行此代码时,我的 Pycharm 编译器返回

File "C:/Users/Matthew/PycharmProjects/Project_Euler/Euler54.py", line 51, in __init__
    self.cards = sorted(args, key=lambda c: -c.rank)
  File "C:/Users/Matthew/PycharmProjects/Project_Euler/Euler54.py", line 51, in <lambda>
    self.cards = sorted(args, key=lambda c: -c.rank)
AttributeError: 'function' object has no attribute 'rank'

我试过改变

self.cards = sorted(args, key=lambda c: -c.rank)

self.cards = sorted(args, key=lambda c: -c.rank())

但这似乎是一个不正确的修复。我不确定如何自行解决此错误。我可以考虑编写一个不同的解决方案,因为视频作者展示的策略对初学者不友好。完整的程序粘贴在下面(我不确定是否应该包括这个,因为我没有正式寻找代码审查。我正在尝试让程序运行以便我可以完成练习)。

import time
from collections import namedtuple

start = time.time()

Card = namedtuple('Card', ['rank', 'suit'])

Ace = 14
King = 13
Queen = 12
Jack = 11
Ten = 10


def map_rank(rank_to_map):
    if rank_to_map == 'A':
        return Ace
    if rank_to_map == 'K':
        return King
    if rank_to_map == 'Q':
        return Queen
    if rank_to_map == 'J':
        return Jack
    if rank_to_map == 'T':
        return Ten
    return int(rank_to_map)


def map_suit(suit_to_map):
    if suit_to_map == 'H':
        return 1
    if suit_to_map == 'D':
        return 2
    if suit_to_map == 'C':
        return 3
    # S = Spades
    return 4


def map_card(card_to_map):
    return Card(rank=map_rank(card_to_map[0]), suit=map_suit(card_to_map[1]))


class Hand(object):

    def __init__(self, *args):
        self.cards = sorted(args, key=lambda c: -c.rank)
        self.duplicates = {}
        for card in self.cards:
            if card.rank in self.duplicates:
                continue
            _duplicates = 0
            for card2 in self.cards:
                if card.rank == card2.rank:
                    _duplicates += 1
            self.duplicates[card.rank] = _duplicates

    def __cmp__(self, other):
        for rule in self.rules():
            rule = getattr(self, rule)
            val = rule(other)
            if val:
                return val

    def rules(self):
        return []

    def _straight(self):
        last_card_rank = False
        for card in self.cards:
            if last_card_rank and last_card_rank != card.rank + 1:
                return False
            last_card_rank = card.rank
        return True

    def _flush(self):
        last_card_suit = False
        for card in self.cards:
            if last_card_suit and last_card_suit != card.suit:
                return False
            last_card_suit = card.suit
        return True

    def _duplicate_count(self, c):
        for rank, value in self.duplicates.items():
            if value == c:
                yield rank


class PokerHand(Hand):
    def rules(self):
        rules = super(PokerHand, self).rules()
        rules.append('royal_flush')
        rules.append('straight_flush')
        rules.append('four_of_a_kind')
        rules.append('full_house')
        rules.append('flush')
        rules.append('straight')
        rules.append('three_of_a_kind')
        rules.append('two_pair')
        rules.append('highest_card')
        return rules

    def royal_flush(self, other):
        h1 = self.cards[0].rank == Ace and self._flush() and self._straight()
        h2 = other.cards[0].rank == Ace and other._flush() and other._straight()
        if h1 and not h2:
            return 1
        if h2 and not h1:
            return -1
        if h1 and h2:
            return 0

    def straight_flush(self, other):
        h1 = self._flush() and self._straight()
        h2 = other._flush() and other._straight()
        if h1 and not h2:
            return 1
        if h2 and not h1:
            return -1
        if h1 and h2:
            return self.highest_card(other)

    def four_of_a_kind(self, other):
        return self._of_a_kind(other, 4)

    def three_of_a_kind(self, other):
        return self._of_a_kind(other, 3)

    def _of_a_kind(self, other, value):
        h1 = [r for r in self._duplicate_count(value)]
        h2 = [r for r in other._duplicate_count(value)]
        if len(h1) > len(h2):
            return 1
        if len(h2) > len(h1):
            return -1
        for h1_c, h2_c in zip(h1, h2):
            if h1_c > h2_c:
                return 1
            if h2_c > h1_c:
                return -1

    def two_pair(self, other):
        return self._of_a_kind(other, 2)

    def full_house(self, other):
        h1_3 = [r for r in self._duplicate_count(3)]
        h2_3 = [r for r in other._duplicate_count(3)]
        h1_2 = [r for r in self._duplicate_count(2)]
        h2_2 = [r for r in other._duplicate_count(2)]
        if h1_3 and h1_2 and not (h2_3 and h2_2):
            return 1
        if h2_3 and h2_2 and not (h1_3 and h1_2):
            return -1
        if h1_3 and h1_2 and h2_3 and h2_2:
            for h1_c, h2_c in zip(h1_3, h2_3):
                if h1_c > h2_c:
                    return 1
                if h2_c > h1_c:
                    return -1
            for h1_c, h2_c in zip(h1_2, h2_2):
                if h1_c > h2_c:
                    return 1
                if h2_c > h1_c:
                    return -1

    def flush(self, other):
        h1 = self._flush
        h2 = other._flush
        if h1 and not h2:
            return 1
        if h2 and not h1:
            return -1
        if h1 and h2:
            return self.highest_card(other)

    def straight(self, other):
        h1 = self._straight
        h2 = other._straight
        if h1 and not h2:
            return 1
        if h2 and not h1:
            return -1
        if h1 and h2:
            return self.highest_card(other)

    def highest_card(self, other):
        for c1,c2 in zip(self.cards, other.cards):
            if c1.rank != c2.rank:
                return 1 if c1.rank > c2.rank else -1
        raise Exception("tie in highest_card")


def gen_hands(s):
    cards = [map_card for c in s.split(' ')]
    return PokerHand(*cards[0:5]), PokerHand(*cards[5:])

p1_wins = 0
with open('poker.txt') as f:
    for line in f:
        h1, h2 = gen_hands(line.strip())
        if h1 > h2:
            p1_wins += 1
print(p1_wins)

如果不添加此处提供的 poker.txt 文件,该程序将不会像粘贴的那样运行。请让我知道在 SE 上为此类属性错误创建帖子是否有误。我不确定是否应该在此站点上发布 Project Euler 的代码。

标签: pythonattr

解决方案


在第 195 行,

cards = [map_card for c in s.split(' ')]

def gen_hands(s):
        cards = [map_card for c in s.split(' ')]
        return PokerHand(*cards[0:5]), PokerHand(*cards[5:])

生成一个包含大量map_card函数引用的列表。这被错误地写为map_card而不是map_card(c)。它应该是

def gen_hands(s):
    cards = [map_card(c) for c in s.split(' ')]
    return PokerHand(*cards[0:5]), PokerHand(*cards[5:])

推荐阅读