I'm trying to write a program that determines whether two words are cognates. I've written two classes: featTup (basically a wrapper around a tuple containing the values of a letter), and featWord (basically a wrapper around of featTup objects.)
(Sorry this is all so long!)
Here's some (hopefully relevant) code:
class featTup(object):
def __init__(self,char):
self.char = char
self.phone_vals = None
self.dia_vals = None
if self.char in phone_codes:
self.phone_vals = phone_feats[phone_codes.index(char)]
elif self.char in dia_codes:
self.dia_vals = dia_feats[dia_codes.index(char)]
...
class featWord(list):
def do_dia(self,char_feats,dia_feats):
#This method handles the changes diacritics make to preceding phones
for val in dia_feats:
if dia_val:
char_feats.change_val(tup,char_feats.index(dia_val),dia_val)
def get_featWord(self):
return self.word_as_feats
def __init__(self,word):
self.word = word
self.word_as_feats = [featTup(char) for char in self.word]
for char in self.word_as_feats:
if char.is_dia():
i = self.word_as_feats.char_index(char)
self.word_as_feats.do_dia(self.word_as_feats[i-1],self.word_as_feats[i])
def word_len(self):
return len(self.get_featWord())
def char_index(self,char):
return self.word_as_feats.index(char)
The issue is that I want to take a list of words and make featWord objects for all of them. I don't know how long each list will be, nor do I know how many characters will be in each word.
More code:
def get_words(text1,text2):
import codecs
textin1 = codecs.open(text1,encoding='utf8')
word_list1 = textin1.readlines()
textin1.close()
textin2 = codecs.open(text2,encoding='utf8')
word_list2 = textin2.readlines()
textin2.close()
print word_list1,word_list2
fixed_words1 = []
fixed_words2 = []
for word in word_list1:
fixed_word = word.replace('\n','')
fixed_words1.append(fixed_word)
for word in word_list2:
fixed_word = word.replace('\n','')
fixed_words2.append(fixed_word)
print fixed_words1,fixed_words2
words1 = [(featWord(word)) for word in fixed_words1]
words2 = [(featWord(word)) for word in fixed_words2]
# for word1 in fixed_words1:
# for x in xrange(len(fixed_words1)):
words1.append(featWord(word))
for word2 in fixed_words2:
#for x in xrange(len(fixed_words2)):
words2.append(featWord(word))
print words1
#words1 = [featWord(word) for word in fixed_words1]
#words2 = [featWord(word) for word in fixed_words2]
return words1,words2
def get_cog_dict(text1,text2,threshold=10,print_results=True):
#This is the final method, running are_cog over all words in
#both lists.
word_list1,word_list2 = get_words(text1,text2)
print word_list1, word_list2
As it stands, when I call either of these last two methods, I get lists of empty lists; when I instantiate new featWord objects from strings I just give it (e.g. x = featWord("ten"), or whatever) it works fine. A relevant thing is that featWord seems to return an empty list instead of (when I instantiate featWord from IDLE, as above, it comes back as a list of featTup instances, which is good). I'm not sure why/if that's the problem.
It seems to me that (at least part of) my problem stems from improperly initializing the featWord. I'm constructing them, or whatever, but not assigning them names. I've tried just about everything I can think of (as the commented-out sections prove), and I'm stumped. There're answers on here about using dictionaries to name class instances and such, but since I can't pre-define a dictionary (each word and wordlist is potentially a different length), I'm not sure what to do.
Any help would be GREATLY appreciated. I'm kind of driving myself insane over here. Thanks.
your featWord class derives from list, but you never append anything to self, and you have overridden __init__, so lists __init__ never gets called, too.
So a featWord instance is just an empty list with some attributes and methods.
Their __repr__ is list's __repr__, that's why a list of featwords displays as a list of empty lists.
So: implement a meaningful __repr__, do not subclass from list, append something meaninful to self. Any of that will solve your problem.
Related
I don't think the design below is a good idea. However, this is for an exam, and they require a list (technically an array, because Cambridge is very biased in favour of VB and Pascal) where each element is of a certain class type. Here is the class diagram:
Also, the board demands that all attributes be private even though -- based on my rudimentary knowledge of Python -- "private" attributes aren't really a thing. Here is the code exhibiting the problem:
class Question():
def __init__(self):
self.__QuestionID = ""
self.__QuestionText = ""
self.__Answer = ""
self.__Mark = ""
self.__Topic = ""
class Test():
def __init__(self):
self.__TestdID = ""
self.__Questions = []
self.__NumOfQs = None
self.__MaxMarks = 0
self.__Level = None
self.__DateSet = None
def DesignTest(self):
self.__NumOfQs = int(self.__NumOfQs)
self.__Questions = [Question for x in range(self.__NumOfQs)] `
for i in self.__Questions:
i = Question()
i.SetQuestion()
The last four lines are my problem area: I don't know how to implement the list of instance objects.
You are correct: Python doesn't have explicit private and public attributes.
I'm not sure what you're trying to do in the final for loop: you already have a list, except that you filled it with the class object itself, instead of instances. Try this:
self.__Questions = [Question() for x in range(self.__NumOfQs)]
Now you have a list of instance objects. If you need to call SetQuestion on each, you already have it set up in that loop:
for i in self.__Questions:
i.SetQuestion()
You can just initiate them in the list itself.
use
self.__Questions = [Question() for x in range(self.__NumOfQs)]
then also try :
for i in self.__Questions:
i.SetQuestion()
The line
self.__Questions = [Question for x in range(self.__NumOfQs)]
will create a list with uninstanciated objects, as many as you have in self.__NumOfQs. If you want to create instanciated objects, just do
self.__Questions = [Question() for x in range(self.__NumOfQs)]
instead and skip the last lines, or skip the line above and do
for i in self.__NumOfQs:
self.__Questions.append(Question())
If i understand correctly you just want a list containing instances of the Question class? You can simply instantiate them in the list generation.
self.__Questions = [Question() for x in range(self.__NumOfQs)]
You can then finish with:
for i in self.__Questions:
i.SetQuestion()
I wanted to remove a substring from a string, for example "a" in "a,b,c" and then return "b,c" to me, it does not matter what's the order of a in string(like "a,b,c", "b,a,c", and so one).
DELIMITER = ","
def remove(member, members_string):
"""removes target from string"""
members = members_string.split(DELIMITER)
members.remove(member)
return DELIMITER.join(members)
print remove("a","b,a,c")
output: b,c
The above function is working as it is expected.
My question is that accidently I modified my code, and it looks as:
def remove_2(member, members_string):
"""removes target from string"""
members = members_string.split(DELIMITER).remove(member)
return DELIMITER.join(members)
You can see that I modified
members = members_string.split(DELIMITER)
members.remove(member)
to
members = members_string.split(DELIMITER).remove(member)
after that the method is broken, it throws
Traceback (most recent call last):
File "test.py", line 15, in <module>
remove_2("a","b,a,c")
File "test.py", line 11, in remove_2
return DELIMITER.join(members)
TypeError
Based on my understanding, members_string.split(DELIMITER) is a list, and invokes remove() is allowed and it should return the new list and stores into members, but
when I print members_string.split(DELIMITER) it returns None, it explains why throws TypeError, my question is , why it returns None other than a list with elements "b" and "c"?
remove() does not return anything. It modifies the list it's called on (lists are mutable, so it would be a major waste of cpu time and memory to create a new list) so returning the same list would be somewhat pointless.
This was already answered here.
Quote from the pythondocs:
You might have noticed that methods like insert, remove or sort that only modify the list have no return value printed – they return the default None. This is a design principle for all mutable data structures in Python.
Mutable objects like lists can be manipulated under the hood via their data-manipulation methods, like remove(),insert(),add().
Immutable objects like strings always return a copy of themselves from their data-manipulation methods, like with replace() or upper().
Method chaining
The next sample shows that your intended method-chaining works with strings:
# Every replace() call is catching a different case from
# member_string like
# a,b,member
# member,b,c
# a,member,c
DELIMITER = ","
def remove(member, member_string):
members = member_string.replace(DELIMITER + member, '').replace(member + DELIMITER, '').replace(DELIMITER + member + DELIMITER, '').upper()
return members
# puts out B,C
print remove("a","b,a,c")
List comprehension
Now for clever lists manipulation (it is even faster than for-looping) the pythonians invented a different feature named list comprehension. You can read about it in python documentation.
DELIMITER = ","
def remove(member, members_string):
members = [m.upper() for m in members_string.split(DELIMITER) if m != member]
return DELIMITER.join(members)
# puts out B,C
print remove("a","b,a,c")
In addition you could google for generators or look into pythondocs. But don't know about that a lot.
BTW, flame me down as a noob but, I hate it when they call python a beginner language, as above list-comprehension looks easy, it could be intimidating for a beginner, couldn't it?
A large entity (list) is created in one method (foo) and bound to self.result.
The attempt to access this entity in a second method (transmit) fails starting at a certain size (something between 150,000 and 155,000 characters in the list). Printing (print self.result) from inside transmit leaves me with None.
I guess this is important: self.foo is directly called in a separate thread.
Please help. How do I get such "large" entity from a separate thread back into the main thread without such limitation?
...
def apply(self):
self.get_data()
self.start_foo_thread()
def start_foo_thread(self):
self.foo_thread = threading.Thread(target=self.foo)
self.foo_thread.daemon = True
self.progressbar.start()
self.foo_thread.start()
self.master.after(20, self.check_foo_thread)
def check_foo_thread(self):
if self.foo_thread.is_alive():
self.master.after(20, self.check_foo_thread)
else:
self.progressbar.stop()
def foo(self):
s = self.stringinput
n = self.numberinput
list = multiply_str_into_list(s, n)
self.result = list_to_text(list)
print self.result # output is not None
def transmit(self):
print self.result # output is None for more than about 155,000 characters in the list
return self.result
def multiply_str_into_list(string, n): #takes a string and multiplies it by n and writes into list
n_string = []
for i in range(0,n):
n_string.append(string)
return n_string
def list_to_text(list): #takes a list as input and joins it into str with each list item on a new line
a = '\n'.join(list)
return a
You don't really provide enough information to reproduce the problem, let alone debug it, but my guess is that at some point, you are doing something like this:
self.result = self.result.append(x)
Since .append() modifies the list in place, and returns None, this will clobber the reference to the list. It needn't be .append() either -- all of the methods that mutate lists return None.
As to why it is happening only at a certain size, perhaps you have some code like the above that is triggered only at a certain size, or else it is a red herring.
I am creating a word parsing class and I keep getting a
bound method Word_Parser.sort_word_list of <__main__.Word_Parser instance at 0x1037dd3b0>
error when I run this:
class Word_Parser:
"""docstring for Word_Parser"""
def __init__(self, sentences):
self.sentences = sentences
def parser(self):
self.word_list = self.sentences.split()
def sort_word_list(self):
self.sorted_word_list = self.word_list.sort()
def num_words(self):
self.num_words = len(self.word_list)
test = Word_Parser("mary had a little lamb")
test.parser()
test.sort_word_list()
test.num_words()
print test.word_list
print test.sort_word_list
print test.num_words
There's no error here. You're printing a function, and that's what functions look like.
To actually call the function, you have to put parens after that. You're already doing that above. If you want to print the result of calling the function, just have the function return the value, and put the print there. For example:
print test.sort_word_list()
On the other hand, if you want the function to mutate the object's state, and then print the state some other way, that's fine too.
Now, your code seems to work in some places, but not others; let's look at why:
parser sets a variable called word_list, and you later print test.word_list, so that works.
sort_word_list sets a variable called sorted_word_list, and you later print test.sort_word_list—that is, the function, not the variable. So, you see the bound method. (Also, as Jon Clements points out, even if you fix this, you're going to print None, because that's what sort returns.)
num_words sets a variable called num_words, and you again print the function—but in this case, the variable has the same name as the function, meaning that you're actually replacing the function with its output, so it works. This is probably not what you want to do, however.
(There are cases where, at first glance, that seems like it might be a good idea—you only want to compute something once, and then access it over and over again without constantly recomputing that. But this isn't the way to do it. Either use a #property, or use a memoization decorator.)
This problem happens as a result of calling a method without brackets. Take a look at the example below:
class SomeClass(object):
def __init__(self):
print 'I am starting'
def some_meth(self):
print 'I am a method()'
x = SomeClass()
''' Not adding the bracket after the method call would result in method bound error '''
print x.some_meth
''' However this is how it should be called and it does solve it '''
x.some_meth()
You have an instance method called num_words, but you also have a variable called num_words. They have the same name. When you run num_words(), the function replaces itself with its own output, which probably isn't what you want to do. Consider returning your values.
To fix your problem, change def num_words to something like def get_num_words and your code should work fine. Also, change print test.sort_word_list to print test.sorted_word_list.
For this thing you can use #property as an decorator, so you could use instance methods as attributes. For example:
class Word_Parser:
def __init__(self, sentences):
self.sentences = sentences
#property
def parser(self):
self.word_list = self.sentences.split()
#property
def sort_word_list(self):
self.sorted_word_list = self.word_list.sort()
#property
def num_words(self):
self.num_words = len(self.word_list)
test = Word_Parser("mary had a little lamb")
test.parser()
test.sort_word_list()
test.num_words()
print test.word_list
print test.sort_word_list
print test.num_words
so you can use access the attributes without calling (i.e., without the ()).
I think you meant print test.sorted_word_list instead of print test.sort_word_list.
In addition list.sort() sorts a list in place and returns None, so you probably want to change sort_word_list() to do the following:
self.sorted_word_list = sorted(self.word_list)
You should also consider either renaming your num_words() function, or changing the attribute that the function assigns to, because currently you overwrite the function with an integer on the first call.
The syntax problem is shadowing method and variable names. In the current version sort_word_list() is a method, and sorted_word_list is a variable, whereas num_words is both. Also, list.sort() modifies the list and replaces it with a sorted version; the sorted(list) function actually returns a new list.
But I suspect this indicates a design problem. What's the point of calls like
test.parser()
test.sort_word_list()
test.num_words()
which don't do anything? You should probably just have the methods figure out whether the appropriate counting and/or sorting has been done, and, if appropriate, do the count or sort and otherwise just return something.
E.G.,
def sort_word_list(self):
if self.sorted_word_list is not None:
self.sorted_word_list = sorted(self.word_list)
return self.sorted_word_list
(Alternately, you could use properties.)
Your helpful comments led me to the following solution:
class Word_Parser:
"""docstring for Word_Parser"""
def __init__(self, sentences):
self.sentences = sentences
def parser(self):
self.word_list = self.sentences.split()
word_list = []
word_list = self.word_list
return word_list
def sort_word_list(self):
self.sorted_word_list = sorted(self.sentences.split())
sorted_word_list = self.sorted_word_list
return sorted_word_list
def get_num_words(self):
self.num_words = len(self.word_list)
num_words = self.num_words
return num_words
test = Word_Parser("mary had a little lamb")
test.parser()
test.sort_word_list()
test.get_num_words()
print test.word_list
print test.sorted_word_list
print test.num_words
and returns:
['mary', 'had', 'a', 'little', 'lamb']
['a', 'had', 'lamb', 'little', 'mary']
5
Thank you all.
Bound method error also occurs (in a Django app for instnce) , if you do a thing as below:
class Products(models.Model):
product_category = models.ForeignKey(ProductCategory, on_delete=models.Protect)
def product_category(self)
return self.product_category
If you name a method, same way you named a field.
I asked a similar, yet lousy, question very late last night (Access to instance variable, but not instance method in Python) that caused a fair bit of confusion. I'd delete it if I could, but I can't.
I now can ask my question more clearly.
Background: I'm trying to build a black-jack game to learn python syntax. Each hand is an instance of the Hand class and I'm now at the point where I'm trying to allow for hands to be split. So, when it comes time for a hand to be split, I need to create two new hand instances. Given that further splits are possible, and I want to reuse the same methods for re-splitting hands. I therefore (I think) need to dynamically instantiate the Hand class.
Following is a code snippet I'm using to block out the mechanics:
import os
os.system("clear")
class Hand():
instances=[]
def __init__(self, hand_a, name):
Hand.instances.append(self)
self.name = name
self.hand_a = hand_a
def show_hand(self):
ln = len(self.hand_a)
for x in range(ln):
print self.hand_a[x]
class Creation():
def __init__(self):
pass
def create_name(self):
hil = len(Hand.instances)
new_name = 'hand_' + str(hil + 1)
return(new_name)
def new_instance(self):
new_dict = {0: 'Ace of Clubs', 1: '10 of Diamonds'}
new_hand_name = {}
new_hand_name.setdefault(self.create_name(), None)
print new_hand_name
new_hand_name[0] = Hand(new_dict, self.create_name())
print new_hand_name[0]
hand = Hand("blah", 'hand')
hand_z = Hand("blah_z", 'hand_z')
creation = Creation()
creation.new_instance()
here is the output:
{'hand_3': None}
<__main__.Hand instance at 0x10e0f06c8>
With regard to the instance created by the following statement:
new_hand_name[0] = Hand(new_dict, self.create_name)
Is new_hand_name[0] new the variable that refers to the instance?
Or, is hand_3 the variable?
i.e. when calling an instance method, can I use hand_3.show_hand()?
First, to answer your questions: new_hand_name[0] is the variable that refers to the instance- more specifically, it is the value in the new_hand_name dictionary accessed by the key 0. The new_hand_name dictionary, if you printed it, would look like:
{'hand_3': None, 0: <__main__.Hand instance at 0x10e0f06c8>}
Adding the value of "hand_3" to the dictionary is unnecessary, but for that matter, so is the dictionary.
What you really want to do has nothing to do with dynamic instantiation of new classes, which has nothing to do with your problem. The problem is that a Hand might represent a single list of cards, but might also represent a list of lists of cards, each of which have to be played separately. One great way to solve this is to make a separation between a player and a hand, and allow a player to have multiple hands. Imagine this design (I'm also leaving out a lot of the blackjack functionality, but leaving a little in to give you an idea of how to work this in with the rest of the program).
def draw_random_card():
"""
whatever function returns a new card. Might be in a Deck object, depends on
your design
"""
# some code here
class Player:
def __init__(self):
self.hands = []
def deal(self):
"""add a random hand"""
self.hands.append(Hand([draw_random_card(), draw_random_card()]))
def split(self, hand):
"""split the given hand"""
self.hands.remove(hand)
self.hands += hand.split()
class Hand:
def __init__(self, cards):
self.cards = cards
def hit(self):
"""add a random card"""
self.cards.append(draw_random_card())
def split(self):
"""split and return a pair of Hand objects"""
return [Hand(self.cards[0], draw_random_card()),
Hand(self.cards[1], draw_random_card())]
Isn't that simpler?
In response to your comment:
You can refer to any specific hand as self.hands[0] or self.hands[1] within the Players class.
If you want to keep track of a particular hand, you can just pass the hand itself around instead of passing a character string referring to that hand. Like this:
def process_hand(hand):
"""do something to a hand of cards"""
h.hit()
print h.cards()
h.hit()
h = Hand(cards)
process_hand(h)
This is important: modifications you make to the hand in the function work on the actual hand itself. Why put the extra step of passing a string that you then have to look up?
Also note that information specific to each hand, such as the bet, should probably be stored in the Hand class itself.
If you are sure you want to refer to each hand with a specific name (and again, it's not necessary in this case), you just use a dictionary with those names as keys:
self.hands = {}
self.hands["hand1"] = Hand([card1, card2])
self.hands["hand2"] = Hand([card1, card2])
print self.hands["hand1"]
But again, there is probably no good reason to do this. (And note that this is very different than instantiating a new variable "dynamically". It would be a good idea to look into how dictionaries work).